From 57915e6f7fa2bfb094580bd07566eb4dfa87b73e Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 5 Feb 2025 14:08:48 +0800 Subject: [PATCH 1/3] feat: avalon airdrop --- adapters/adapters.go | 2 +- adapters/projects/avalon/claim.go | 98 ++++++++++++++++++++++++++ adapters/projects/avalon/claim_test.go | 40 +++++++++++ 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 adapters/projects/avalon/claim.go create mode 100644 adapters/projects/avalon/claim_test.go diff --git a/adapters/adapters.go b/adapters/adapters.go index e9c0db6..6802156 100644 --- a/adapters/adapters.go +++ b/adapters/adapters.go @@ -70,7 +70,7 @@ type Position struct { TxHash common.Hash } -// Prdiction is used for prediction campaign. +// Prediction is used for prediction campaign. // For examples, see Robinos. type Prediction struct { User common.Address diff --git a/adapters/projects/avalon/claim.go b/adapters/projects/avalon/claim.go new file mode 100644 index 0000000..10dd8fd --- /dev/null +++ b/adapters/projects/avalon/claim.go @@ -0,0 +1,98 @@ +package avalon + +import ( + "context" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/taikoxyz/trailblazer-adapters/adapters" + "github.com/taikoxyz/trailblazer-adapters/adapters/contracts/erc20" +) + +const ( + // TODO: update + AvalonAirdropContractAddress string = "0x46f0a2e45bee8e9ebfdb278ce06caa6af294c349" + AvalonTokenAddress string = "0x46f0a2e45bee8e9ebfdb278ce06caa6af294c349" + AvalonTokenDecimal = 18 + + logTransferSignature string = "Transfer(address,address,uint256)" +) + +type ClaimIndexer struct { + client *ethclient.Client + addresses []common.Address + contract common.Address +} + +func NewClaimIndexer(client *ethclient.Client, contract common.Address, addresses []common.Address) *ClaimIndexer { + return &ClaimIndexer{ + client: client, + addresses: addresses, + contract: contract, + } +} + +var _ adapters.LogIndexer[adapters.Position] = &ClaimIndexer{} + +func (indexer *ClaimIndexer) Addresses() []common.Address { + return indexer.addresses +} + +func (indexer *ClaimIndexer) Index(ctx context.Context, logs ...types.Log) ([]adapters.Position, error) { + var claims []adapters.Position + + for _, l := range logs { + if !indexer.isTransfer(l) { + continue + } + + var transferEvent struct { + Value *big.Int + } + + to := common.BytesToAddress(l.Topics[2].Bytes()[12:]) + from := common.BytesToAddress(l.Topics[1].Bytes()[12:]) + + if from != indexer.contract { + continue + } + + erc20ABI, err := abi.JSON(strings.NewReader(erc20.ABI)) + if err != nil { + return nil, err + } + + err = erc20ABI.UnpackIntoInterface(&transferEvent, "Transfer", l.Data) + if err != nil { + return nil, err + } + + block, err := indexer.client.BlockByNumber(ctx, big.NewInt(int64(l.BlockNumber))) + if err != nil { + return nil, err + } + + claim := &adapters.Position{ + User: to, + TokenAmount: transferEvent.Value, + TokenDecimals: AvalonTokenDecimal, + Token: common.HexToAddress(adapters.TaikoTokenAddress), + BlockTime: block.Time(), + BlockNumber: block.NumberU64(), + TxHash: l.TxHash, + } + + claims = append(claims, *claim) + } + + return claims, nil +} + +func (indexer *ClaimIndexer) isTransfer(l types.Log) bool { + return l.Topics[0].Hex() == crypto.Keccak256Hash([]byte(logTransferSignature)).Hex() +} diff --git a/adapters/projects/avalon/claim_test.go b/adapters/projects/avalon/claim_test.go new file mode 100644 index 0000000..ec8c2ac --- /dev/null +++ b/adapters/projects/avalon/claim_test.go @@ -0,0 +1,40 @@ +package avalon_test + +import ( + "context" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/taikoxyz/trailblazer-adapters/adapters" + "github.com/taikoxyz/trailblazer-adapters/adapters/projects/avalon" +) + +func TestClaimIndexer(t *testing.T) { + taikoRPC := "https://rpc.taiko.xyz" + blocknumber := int64(445053) + + ctx := context.Background() + + client, err := ethclient.Dial(taikoRPC) + require.NoError(t, err) + + indexer := avalon.NewClaimIndexer(client, common.HexToAddress(avalon.AvalonAirdropContractAddress), []common.Address{common.HexToAddress(avalon.AvalonTokenAddress)}) + + logs, err := adapters.GetLogs(ctx, client, indexer.Addresses(), blocknumber) + require.NoError(t, err) + + locks, err := indexer.Index(ctx, logs...) + assert.NoError(t, err) + assert.Len(t, locks, 1) + assert.Equal(t, common.HexToAddress("0xC3204E92B0e7731d75Ad667a93c8Da815BD9Ac61"), locks[0].User) + assert.Equal(t, big.NewInt(2000000000000000000), locks[0].TokenAmount) + assert.Equal(t, adapters.TaikoTokenDecimals, locks[0].TokenDecimals) + assert.Equal(t, common.HexToAddress(adapters.TaikoTokenAddress), locks[0].Token) + assert.Equal(t, uint64(1728390191), locks[0].BlockTime) + assert.Equal(t, uint64(blocknumber), locks[0].BlockNumber) + assert.Equal(t, common.HexToHash("0x95f528b52f0a75176543f516014bbba26e003f1c17c9b9413e936240e3f44650"), locks[0].TxHash) +} From f14306ce1fe65e96e6f73e682c7a43b5155ca035 Mon Sep 17 00:00:00 2001 From: ben Date: Wed, 5 Feb 2025 14:09:45 +0800 Subject: [PATCH 2/3] fix --- adapters/projects/avalon/claim.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapters/projects/avalon/claim.go b/adapters/projects/avalon/claim.go index 10dd8fd..cc19115 100644 --- a/adapters/projects/avalon/claim.go +++ b/adapters/projects/avalon/claim.go @@ -58,7 +58,7 @@ func (indexer *ClaimIndexer) Index(ctx context.Context, logs ...types.Log) ([]ad to := common.BytesToAddress(l.Topics[2].Bytes()[12:]) from := common.BytesToAddress(l.Topics[1].Bytes()[12:]) - if from != indexer.contract { + if from.Hex() != indexer.contract.Hex() { continue } From 66a6c4694cdc6f556cd77d5294fabe467869b273 Mon Sep 17 00:00:00 2001 From: Myron Rotter Date: Wed, 5 Feb 2025 10:21:45 +0100 Subject: [PATCH 3/3] fix: position token address --- adapters/adapters.go | 3 ++- adapters/projects/avalon/claim.go | 18 +++++++++--------- adapters/projects/avalon/claim_test.go | 2 +- cmd/adapter.go | 4 ++++ cmd/cmd.go | 15 +++++++++++++++ 5 files changed, 31 insertions(+), 11 deletions(-) diff --git a/adapters/adapters.go b/adapters/adapters.go index 6802156..0843230 100644 --- a/adapters/adapters.go +++ b/adapters/adapters.go @@ -59,7 +59,8 @@ type Lock struct { TxHash common.Hash } -// Position is used for logx trading campaign +// Position is used for airdrop or trading campaign. +// For examples, see Avalon. type Position struct { User common.Address TokenAmount *big.Int diff --git a/adapters/projects/avalon/claim.go b/adapters/projects/avalon/claim.go index cc19115..8a46a63 100644 --- a/adapters/projects/avalon/claim.go +++ b/adapters/projects/avalon/claim.go @@ -16,9 +16,9 @@ import ( const ( // TODO: update - AvalonAirdropContractAddress string = "0x46f0a2e45bee8e9ebfdb278ce06caa6af294c349" - AvalonTokenAddress string = "0x46f0a2e45bee8e9ebfdb278ce06caa6af294c349" - AvalonTokenDecimal = 18 + AvalonAirdropAddress string = "0x46f0a2e45bee8e9ebfdb278ce06caa6af294c349" + AvalonTokenAddress string = "0x46f0a2e45bee8e9ebfdb278ce06caa6af294c349" + AvalonTokenDecimal = 18 logTransferSignature string = "Transfer(address,address,uint256)" ) @@ -44,6 +44,10 @@ func (indexer *ClaimIndexer) Addresses() []common.Address { } func (indexer *ClaimIndexer) Index(ctx context.Context, logs ...types.Log) ([]adapters.Position, error) { + var transferEvent struct { + Value *big.Int + } + var claims []adapters.Position for _, l := range logs { @@ -51,12 +55,8 @@ func (indexer *ClaimIndexer) Index(ctx context.Context, logs ...types.Log) ([]ad continue } - var transferEvent struct { - Value *big.Int - } - - to := common.BytesToAddress(l.Topics[2].Bytes()[12:]) from := common.BytesToAddress(l.Topics[1].Bytes()[12:]) + to := common.BytesToAddress(l.Topics[2].Bytes()[12:]) if from.Hex() != indexer.contract.Hex() { continue @@ -81,7 +81,7 @@ func (indexer *ClaimIndexer) Index(ctx context.Context, logs ...types.Log) ([]ad User: to, TokenAmount: transferEvent.Value, TokenDecimals: AvalonTokenDecimal, - Token: common.HexToAddress(adapters.TaikoTokenAddress), + Token: common.HexToAddress(AvalonTokenAddress), BlockTime: block.Time(), BlockNumber: block.NumberU64(), TxHash: l.TxHash, diff --git a/adapters/projects/avalon/claim_test.go b/adapters/projects/avalon/claim_test.go index ec8c2ac..2695d8f 100644 --- a/adapters/projects/avalon/claim_test.go +++ b/adapters/projects/avalon/claim_test.go @@ -22,7 +22,7 @@ func TestClaimIndexer(t *testing.T) { client, err := ethclient.Dial(taikoRPC) require.NoError(t, err) - indexer := avalon.NewClaimIndexer(client, common.HexToAddress(avalon.AvalonAirdropContractAddress), []common.Address{common.HexToAddress(avalon.AvalonTokenAddress)}) + indexer := avalon.NewClaimIndexer(client, common.HexToAddress(avalon.AvalonAirdropAddress), []common.Address{common.HexToAddress(avalon.AvalonTokenAddress)}) logs, err := adapters.GetLogs(ctx, client, indexer.Addresses(), blocknumber) require.NoError(t, err) diff --git a/cmd/adapter.go b/cmd/adapter.go index 8b2fde5..2d740df 100644 --- a/cmd/adapter.go +++ b/cmd/adapter.go @@ -23,6 +23,8 @@ const ( RobinosPrediction adapter = "RobinosPrediction" LoopringLock adapter = "LoopringLock" PolarisLP adapter = "PolarisLP" + DoraHacksVoting adapter = "DoraHacksVoting" + AvalonClaim adapter = "AvalonClaim" ) func adapterz() []adapter { @@ -43,5 +45,7 @@ func adapterz() []adapter { RobinosPrediction, LoopringLock, PolarisLP, + DoraHacksVoting, + AvalonClaim, } } diff --git a/cmd/cmd.go b/cmd/cmd.go index 742138e..4a76334 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -13,8 +13,10 @@ import ( "github.com/spf13/cobra" "github.com/taikoxyz/trailblazer-adapters/adapters" nftdeployed "github.com/taikoxyz/trailblazer-adapters/adapters/nft_deployed" + "github.com/taikoxyz/trailblazer-adapters/adapters/projects/avalon" "github.com/taikoxyz/trailblazer-adapters/adapters/projects/conft" "github.com/taikoxyz/trailblazer-adapters/adapters/projects/domains" + "github.com/taikoxyz/trailblazer-adapters/adapters/projects/dorahacks" "github.com/taikoxyz/trailblazer-adapters/adapters/projects/drips" "github.com/taikoxyz/trailblazer-adapters/adapters/projects/gaming" "github.com/taikoxyz/trailblazer-adapters/adapters/projects/izumi" @@ -201,6 +203,19 @@ func executeCommand(p prompt) error { []common.Address{common.HexToAddress(polaris.VaultAddress)}, ) return processLog(ctx, client, indexer, p.Blocknumber) + case DoraHacksVoting: + indexer := dorahacks.NewVotingIndexer( + client, + []common.Address{common.HexToAddress(dorahacks.VotingAddress)}, + ) + return processLog(ctx, client, indexer, p.Blocknumber) + case AvalonClaim: + indexer := avalon.NewClaimIndexer( + client, + common.HexToAddress(avalon.AvalonAirdropAddress), + []common.Address{common.HexToAddress(avalon.AvalonTokenAddress)}, + ) + return processLog(ctx, client, indexer, p.Blocknumber) default: return fmt.Errorf("adapter %s is not supported", p.Adapter)