Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add outstanding token airdrops to REST API #9286

Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ public class AbstractTokenAirdrop implements History {
private Long amount;

@jakarta.persistence.Id
private long receiverAccountId;
private long receiverId;

@jakarta.persistence.Id
private long senderAccountId;
private long senderId;

@jakarta.persistence.Id
private long serialNumber;
Expand All @@ -63,8 +63,8 @@ public class AbstractTokenAirdrop implements History {
@JsonIgnore
public Id getId() {
Id id = new Id();
id.setReceiverAccountId(receiverAccountId);
id.setSenderAccountId(senderAccountId);
id.setReceiverId(receiverId);
id.setSenderId(senderId);
id.setSerialNumber(serialNumber);
id.setTokenId(tokenId);
return id;
Expand All @@ -75,8 +75,8 @@ public static class Id implements Serializable {
@Serial
private static final long serialVersionUID = -8165098238647325621L;

private long receiverAccountId;
private long senderAccountId;
private long receiverId;
private long senderId;
private long serialNumber;
private long tokenId;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -932,8 +932,8 @@ public DomainWrapper<SidecarFile, SidecarFile.SidecarFileBuilder> sidecarFile()
public DomainWrapper<TokenAirdrop, TokenAirdrop.TokenAirdropBuilder<?, ?>> tokenAirdrop(TokenTypeEnum type) {
long timestamp = timestamp();
var builder = TokenAirdrop.builder()
.receiverAccountId(id())
.senderAccountId(id())
.receiverId(id())
.senderId(id())
.state(TokenAirdropStateEnum.PENDING)
.timestampRange(Range.atLeast(timestamp))
.tokenId(id());
Expand All @@ -951,8 +951,8 @@ public DomainWrapper<SidecarFile, SidecarFile.SidecarFileBuilder> sidecarFile()
TokenTypeEnum type) {
long timestamp = timestamp();
var builder = TokenAirdropHistory.builder()
.receiverAccountId(id())
.senderAccountId(id())
.receiverId(id())
.senderId(id())
.state(TokenAirdropStateEnum.PENDING)
.timestampRange(Range.closedOpen(timestamp, timestamp + 10))
.tokenId(id());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ public void doUpdateTransaction(Transaction transaction, RecordItem recordItem)

var tokenAirdrop = new TokenAirdrop();
tokenAirdrop.setState(state);
tokenAirdrop.setReceiverAccountId(receiver.getId());
tokenAirdrop.setSenderAccountId(sender.getId());
tokenAirdrop.setReceiverId(receiver.getId());
tokenAirdrop.setSenderId(sender.getId());
tokenAirdrop.setTimestampRange(Range.atLeast(recordItem.getConsensusTimestamp()));

TokenID tokenId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ protected void doUpdateTransaction(Transaction transaction, RecordItem recordIte

var tokenAirdrop = new TokenAirdrop();
tokenAirdrop.setState(TokenAirdropStateEnum.PENDING);
tokenAirdrop.setReceiverAccountId(receiver.getId());
tokenAirdrop.setSenderAccountId(sender.getId());
tokenAirdrop.setReceiverId(receiver.getId());
tokenAirdrop.setSenderId(sender.getId());
tokenAirdrop.setTimestampRange(Range.atLeast(recordItem.getConsensusTimestamp()));

TokenID tokenId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ call create_temp_table_safe('nft', 'token_id', 'serial_number');
call create_temp_table_safe('node', 'node_id');
call create_temp_table_safe('schedule', 'schedule_id');
call create_temp_table_safe('token_account', 'account_id', 'token_id');
call create_temp_table_safe('token_airdrop', 'receiver_account_id', 'sender_account_id', 'serial_number', 'token_id');
call create_temp_table_safe('token_airdrop', 'receiver_id', 'sender_id', 'serial_number', 'token_id');
call create_temp_table_safe('token_allowance', 'owner', 'spender', 'token_id');
call create_temp_table_safe('token', 'token_id');
call create_temp_table_safe('topic_message_lookup', 'topic_id', 'partition');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ create type airdrop_state as enum ('CANCELLED', 'CLAIMED', 'PENDING');
create table if not exists token_airdrop
(
amount bigint,
receiver_account_id bigint not null,
sender_account_id bigint not null,
receiver_id bigint not null,
sender_id bigint not null,
serial_number bigint not null,
state airdrop_state not null default 'PENDING',
timestamp_range int8range not null,
token_id bigint not null
);

create unique index if not exists token_airdrop__sender_id on token_airdrop (sender_account_id, receiver_account_id, token_id, serial_number);
create index if not exists token_airdrop__receiver_id on token_airdrop (receiver_account_id, sender_account_id, token_id, serial_number);
create unique index if not exists token_airdrop__sender_id on token_airdrop (sender_id, receiver_id, token_id, serial_number);
create index if not exists token_airdrop__receiver_id on token_airdrop (receiver_id, sender_id, token_id, serial_number);
steven-sheehy marked this conversation as resolved.
Show resolved Hide resolved

create table if not exists token_airdrop_history
(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ call create_distributed_table_safe('nft_allowance_temp', 'owner', 'nft_allowance
call create_distributed_table_safe('nft_temp', 'token_id', 'nft');
call create_distributed_table_safe('schedule_temp', 'schedule_id', 'schedule');
call create_distributed_table_safe('token_account_temp', 'account_id', 'token_account');
call create_distributed_table_safe('token_airdrop_temp', 'receiver_account_id', 'token_airdrop');
call create_distributed_table_safe('token_airdrop_temp', 'receiver_id', 'token_airdrop');
call create_distributed_table_safe('token_allowance_temp', 'owner', 'token_allowance');
call create_distributed_table_safe('dissociate_token_transfer', 'token_id', 'nft');
call create_distributed_table_safe('token_temp', 'token_id', 'token');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ create type airdrop_state as enum ('CANCELLED', 'CLAIMED', 'PENDING');
create table if not exists token_airdrop
(
amount bigint,
receiver_account_id bigint not null,
sender_account_id bigint not null,
receiver_id bigint not null,
sender_id bigint not null,
serial_number bigint not null,
state airdrop_state not null default 'PENDING',
timestamp_range int8range not null,
Expand All @@ -18,9 +18,9 @@ create table if not exists token_airdrop_history
);
comment on table token_airdrop_history is 'History of token airdrops';

select create_distributed_table('token_airdrop', 'receiver_account_id', colocate_with => 'entity');
select create_distributed_table('token_airdrop_history', 'receiver_account_id', colocate_with => 'token_airdrop');
select create_distributed_table('token_airdrop', 'receiver_id', colocate_with => 'entity');
select create_distributed_table('token_airdrop_history', 'receiver_id', colocate_with => 'token_airdrop');

create unique index if not exists token_airdrop__sender_id on token_airdrop (sender_account_id, receiver_account_id, token_id, serial_number);
create index if not exists token_airdrop__receiver_id on token_airdrop (receiver_account_id, sender_account_id, token_id, serial_number);
create unique index if not exists token_airdrop__sender_id on token_airdrop (sender_id, receiver_id, token_id, serial_number);
create index if not exists token_airdrop__receiver_id on token_airdrop (receiver_id, sender_id, token_id, serial_number);
create index if not exists token_airdrop_history__timestamp_range on token_airdrop_history using gist (timestamp_range);
Original file line number Diff line number Diff line change
Expand Up @@ -3609,16 +3609,16 @@ void tokenAirdrop(TokenAirdropStateEnum airdropType) {
var expectedPendingFungible = domainBuilder
.tokenAirdrop(TokenTypeEnum.FUNGIBLE_COMMON)
.customize(t -> t.amount(pendingAmount)
.receiverAccountId(RECEIVER.getAccountNum())
.senderAccountId(PAYER.getAccountNum())
.receiverId(RECEIVER.getAccountNum())
.senderId(PAYER.getAccountNum())
.state(TokenAirdropStateEnum.PENDING)
.timestampRange(Range.atLeast(airdropTimestamp))
.tokenId(TOKEN_ID.getTokenNum()))
.get();
var expectedPendingNft = domainBuilder
.tokenAirdrop(TokenTypeEnum.NON_FUNGIBLE_UNIQUE)
.customize(t -> t.receiverAccountId(RECEIVER.getAccountNum())
.senderAccountId(PAYER.getAccountNum())
.customize(t -> t.receiverId(RECEIVER.getAccountNum())
.senderId(PAYER.getAccountNum())
.serialNumber(SERIAL_NUMBER_1)
.state(TokenAirdropStateEnum.PENDING)
.timestampRange(Range.atLeast(airdropTimestamp))
Expand Down Expand Up @@ -3738,15 +3738,15 @@ void tokenAirdropUpdateState(TokenAirdropStateEnum airdropType) {
var expectedPendingFungible = domainBuilder
.tokenAirdrop(TokenTypeEnum.FUNGIBLE_COMMON)
.customize(t -> t.amount(pendingAmount)
.receiverAccountId(RECEIVER.getAccountNum())
.senderAccountId(PAYER.getAccountNum())
.receiverId(RECEIVER.getAccountNum())
.senderId(PAYER.getAccountNum())
.timestampRange(Range.atLeast(airdropTimestamp))
.tokenId(TOKEN_ID.getTokenNum()))
.get();
var expectedPendingNft = domainBuilder
.tokenAirdrop(TokenTypeEnum.NON_FUNGIBLE_UNIQUE)
.customize(t -> t.receiverAccountId(RECEIVER.getAccountNum())
.senderAccountId(PAYER.getAccountNum())
.customize(t -> t.receiverId(RECEIVER.getAccountNum())
.senderId(PAYER.getAccountNum())
.serialNumber(SERIAL_NUMBER_1)
.timestampRange(Range.atLeast(airdropTimestamp))
.tokenId(nftTokenId.getTokenNum()))
Expand Down Expand Up @@ -3843,15 +3843,15 @@ void tokenAirdropPartialData(TokenAirdropStateEnum airdropType) {
.tokenAirdrop(TokenTypeEnum.FUNGIBLE_COMMON)
// Amount will be null when there is no pending airdrop
.customize(t -> t.amount(null)
.receiverAccountId(RECEIVER.getAccountNum())
.senderAccountId(PAYER.getAccountNum())
.receiverId(RECEIVER.getAccountNum())
.senderId(PAYER.getAccountNum())
.timestampRange(Range.atLeast(updateTimestamp))
.tokenId(TOKEN_ID.getTokenNum()))
.get();
var expectedPendingNft = domainBuilder
.tokenAirdrop(TokenTypeEnum.NON_FUNGIBLE_UNIQUE)
.customize(t -> t.receiverAccountId(RECEIVER.getAccountNum())
.senderAccountId(PAYER.getAccountNum())
.customize(t -> t.receiverId(RECEIVER.getAccountNum())
.senderId(PAYER.getAccountNum())
.serialNumber(SERIAL_NUMBER_1)
.timestampRange(Range.atLeast(updateTimestamp))
.tokenId(nftTokenId.getTokenNum()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2722,8 +2722,8 @@ void onTokenAirdrop() {
var updatedAmountAirdrop = domainBuilder
.tokenAirdrop(TokenTypeEnum.FUNGIBLE_COMMON)
.customize(a -> a.amount(newAmount)
.receiverAccountId(tokenAirdrop.getReceiverAccountId())
.senderAccountId(tokenAirdrop.getSenderAccountId())
.receiverId(tokenAirdrop.getReceiverId())
.senderId(tokenAirdrop.getSenderId())
.tokenId(tokenAirdrop.getTokenId())
.timestampRange(Range.atLeast(domainBuilder.timestamp())))
.get();
Expand Down Expand Up @@ -2768,16 +2768,16 @@ void onTokenAirdropUpdate(TokenAirdropStateEnum state, int commitIndex) {
.customize(a -> a.state(state)
.amount(null) // Record files that change state do not include PendingAirdropValue so remove the
// amount here
.receiverAccountId(tokenAirdrop.getReceiverAccountId())
.senderAccountId(tokenAirdrop.getSenderAccountId())
.receiverId(tokenAirdrop.getReceiverId())
.senderId(tokenAirdrop.getSenderId())
.tokenId(tokenAirdrop.getTokenId())
.timestampRange(Range.atLeast(domainBuilder.timestamp())))
.get();
var nftAirdropUpdateState = domainBuilder
.tokenAirdrop(TokenTypeEnum.NON_FUNGIBLE_UNIQUE)
.customize(a -> a.state(state)
.receiverAccountId(nftAirdrop.getReceiverAccountId())
.senderAccountId(nftAirdrop.getSenderAccountId())
.receiverId(nftAirdrop.getReceiverId())
.senderId(nftAirdrop.getSenderId())
.serialNumber(nftAirdrop.getSerialNumber())
.tokenId(nftAirdrop.getTokenId())
.timestampRange(Range.atLeast(domainBuilder.timestamp())))
Expand Down Expand Up @@ -2814,16 +2814,16 @@ void onTokenAirdropPartialUpdate(TokenAirdropStateEnum state) {
.customize(a -> a.state(state)
.amount(null) // Record files that change state do not include PendingAirdropValue so remove the
// amount here
.receiverAccountId(tokenAirdrop.getReceiverAccountId())
.senderAccountId(tokenAirdrop.getSenderAccountId())
.receiverId(tokenAirdrop.getReceiverId())
.senderId(tokenAirdrop.getSenderId())
.tokenId(tokenAirdrop.getTokenId())
.timestampRange(Range.atLeast(domainBuilder.timestamp())))
.get();
var nftAirdropUpdateState = domainBuilder
.tokenAirdrop(TokenTypeEnum.NON_FUNGIBLE_UNIQUE)
.customize(a -> a.state(state)
.receiverAccountId(nftAirdrop.getReceiverAccountId())
.senderAccountId(nftAirdrop.getSenderAccountId())
.receiverId(nftAirdrop.getReceiverId())
.senderId(nftAirdrop.getSenderId())
.serialNumber(nftAirdrop.getSerialNumber())
.tokenId(nftAirdrop.getTokenId())
.timestampRange(Range.atLeast(domainBuilder.timestamp())))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ void updateTransactionSuccessfulFungiblePendingAirdrop() {
verify(entityListener).onTokenAirdrop(tokenAirdrop.capture());
assertThat(tokenAirdrop.getValue())
.returns(amount, TokenAirdrop::getAmount)
.returns(receiver.getAccountNum(), TokenAirdrop::getReceiverAccountId)
.returns(sender.getAccountNum(), TokenAirdrop::getSenderAccountId)
.returns(receiver.getAccountNum(), TokenAirdrop::getReceiverId)
.returns(sender.getAccountNum(), TokenAirdrop::getSenderId)
.returns(0L, TokenAirdrop::getSerialNumber)
.returns(PENDING, TokenAirdrop::getState)
.returns(Range.atLeast(timestamp), TokenAirdrop::getTimestampRange)
Expand Down Expand Up @@ -130,8 +130,8 @@ void updateTransactionSuccessfulNftPendingAirdrop() {
verify(entityListener).onTokenAirdrop(tokenAirdrop.capture());
assertThat(tokenAirdrop.getValue())
.returns(null, TokenAirdrop::getAmount)
.returns(receiver.getAccountNum(), TokenAirdrop::getReceiverAccountId)
.returns(sender.getAccountNum(), TokenAirdrop::getSenderAccountId)
.returns(receiver.getAccountNum(), TokenAirdrop::getReceiverId)
.returns(sender.getAccountNum(), TokenAirdrop::getSenderId)
.returns(PENDING, TokenAirdrop::getState)
.returns(Range.atLeast(timestamp), TokenAirdrop::getTimestampRange)
.returns(token.getTokenNum(), TokenAirdrop::getTokenId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ void cancelAirdrop(TokenTypeEnum tokenType) {

verify(entityListener).onTokenAirdrop(tokenAirdrop.capture());
assertThat(tokenAirdrop.getValue())
.returns(receiver.getNum(), TokenAirdrop::getReceiverAccountId)
.returns(sender.getNum(), TokenAirdrop::getSenderAccountId)
.returns(receiver.getNum(), TokenAirdrop::getReceiverId)
.returns(sender.getNum(), TokenAirdrop::getSenderId)
.returns(TokenAirdropStateEnum.CANCELLED, TokenAirdrop::getState)
.returns(Range.atLeast(timestamp), TokenAirdrop::getTimestampRange)
.returns(token.getTokenNum(), TokenAirdrop::getTokenId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ void claimAirdrop(TokenTypeEnum tokenType) {

verify(entityListener).onTokenAirdrop(tokenAirdrop.capture());
assertThat(tokenAirdrop.getValue())
.returns(receiver.getNum(), TokenAirdrop::getReceiverAccountId)
.returns(sender.getNum(), TokenAirdrop::getSenderAccountId)
.returns(receiver.getNum(), TokenAirdrop::getReceiverId)
.returns(sender.getNum(), TokenAirdrop::getSenderId)
.returns(TokenAirdropStateEnum.CLAIMED, TokenAirdrop::getState)
.returns(Range.atLeast(timestamp), TokenAirdrop::getTimestampRange)
.returns(token.getTokenNum(), TokenAirdrop::getTokenId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class Constants {

public static final String ACCOUNT_ID = "account.id";
public static final String RECEIVER_ID = "receiver.id";
public static final String SENDER_ID = "sender.id";
public static final String SERIAL_NUMBER = "serialnumber";
public static final String TOKEN_ID = "token.id";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,30 @@

import org.apache.commons.lang3.StringUtils;

public record IntegerRangeParameter(RangeOperator operator, Integer value) implements RangeParameter<Integer> {
public record NumberRangeParameter(RangeOperator operator, Long value) implements RangeParameter<Long> {

public static final IntegerRangeParameter EMPTY = new IntegerRangeParameter(null, null);
public static final NumberRangeParameter EMPTY = new NumberRangeParameter(null, null);

public static IntegerRangeParameter valueOf(String valueRangeParam) {
public static NumberRangeParameter valueOf(String valueRangeParam) {
if (StringUtils.isBlank(valueRangeParam)) {
return EMPTY;
}

var splitVal = valueRangeParam.split(":");
return switch (splitVal.length) {
case 1 -> new IntegerRangeParameter(RangeOperator.EQ, Integer.valueOf(splitVal[0]));
case 2 -> new IntegerRangeParameter(RangeOperator.of(splitVal[0]), Integer.valueOf(splitVal[1]));
case 1 -> new NumberRangeParameter(RangeOperator.EQ, getNumberValue(splitVal[0]));
case 2 -> new NumberRangeParameter(RangeOperator.of(splitVal[0]), getNumberValue(splitVal[1]));
default -> throw new IllegalArgumentException(
"Invalid range operator %s. Should have format rangeOperator:Integer".formatted(valueRangeParam));
"Invalid range operator %s. Should have format rangeOperator:Number".formatted(valueRangeParam));

Check warning on line 35 in hedera-mirror-rest-java/src/main/java/com/hedera/mirror/restjava/common/NumberRangeParameter.java

View check run for this annotation

Codecov / codecov/patch

hedera-mirror-rest-java/src/main/java/com/hedera/mirror/restjava/common/NumberRangeParameter.java#L34-L35

Added lines #L34 - L35 were not covered by tests
};
}

private static long getNumberValue(String number) {
var value = Long.parseLong(number);
if (value < 0) {
throw new IllegalArgumentException("Invalid range value: " + number);
}

return value;
}
}
Loading
Loading