-
Notifications
You must be signed in to change notification settings - Fork 81
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from taikoxyz/nft-trail
feat: add nft trails whitelist
- Loading branch information
Showing
25 changed files
with
615 additions
and
93 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# Trailblazer Adapters | ||
|
||
## Adding a New Protocol Adapter | ||
|
||
### 1. Add your project under adapters/projects folder | ||
|
||
### 2. Write an indexer in the new folder | ||
|
||
An example adapter for tracking OrderFulfilled Event on the OKX marketplace can be seen [here](./adapters/projects/okx/order_fulfilled.go) | ||
|
||
### 3. Test out the adapter by adding it to the cli | ||
|
||
An example on how to add a new adapter to the cli is [here](./cmd/README.md) | ||
|
||
### 4. Create a Pull Request | ||
|
||
Create a pull request (PR) on GitHub to merge your changes into the main branch. Provide a clear description of the changes and the protocol added. | ||
|
||
### 5. Review and Merge | ||
|
||
Wait for the PR to be reviewed by the maintainers. Once approved, your changes will be merged, and the protocol info will be added to trailblazers. | ||
|
||
For further details, refer to the official documentation or contact the maintainers for support. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package blocks | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/ethereum/go-ethereum" | ||
"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/ethclient" | ||
"github.com/taikoxyz/trailblazer-adapters/adapters" | ||
"github.com/taikoxyz/trailblazer-adapters/adapters/contracts/erc165" | ||
) | ||
|
||
type NftDeployedIndexer struct{} | ||
|
||
// NewNftDeployedIndexer creates a new NftDeployedIndexer. | ||
func NewNftDeployedIndexer() *NftDeployedIndexer { | ||
return &NftDeployedIndexer{} | ||
} | ||
|
||
const ( | ||
ERC721InterfaceID = "0x80ac58cd" // ERC721 interface ID | ||
ERC1155InterfaceID = "0xd9b67a26" // ERC1155 interface ID | ||
) | ||
|
||
func supportsInterface(contractAddress common.Address, client *ethclient.Client, interfaceID string) (bool, error) { | ||
parsedABI, err := abi.JSON(strings.NewReader(erc165.ABI)) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
interfaceIDBytes := common.FromHex(interfaceID) | ||
if len(interfaceIDBytes) != 4 { | ||
return false, fmt.Errorf("invalid interface ID length") | ||
} | ||
|
||
data, err := parsedABI.Pack("supportsInterface", [4]byte{interfaceIDBytes[0], interfaceIDBytes[1], interfaceIDBytes[2], interfaceIDBytes[3]}) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
msg := ethereum.CallMsg{ | ||
To: &contractAddress, | ||
Data: data, | ||
} | ||
|
||
result, err := client.CallContract(context.Background(), msg, nil) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
var supports bool | ||
err = parsedABI.UnpackIntoInterface(&supports, "supportsInterface", result) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
return supports, nil | ||
} | ||
|
||
func (indexer *NftDeployedIndexer) ProcessBlock(ctx context.Context, block *types.Block, client *ethclient.Client) ([]adapters.Whitelist, error) { | ||
txs := block.Transactions() | ||
var result []adapters.Whitelist | ||
|
||
for _, tx := range txs { | ||
receipt, err := client.TransactionReceipt(ctx, tx.Hash()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
sender, err := client.TransactionSender(ctx, tx, block.Hash(), receipt.TransactionIndex) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
to := tx.To() | ||
if to != nil { | ||
continue | ||
} | ||
|
||
isErc721, err := supportsInterface(receipt.ContractAddress, client, ERC721InterfaceID) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if isErc721 { | ||
result = append(result, adapters.Whitelist{ | ||
User: sender, | ||
BlockNumber: block.Number().Uint64(), | ||
Time: block.Time(), | ||
}) | ||
} | ||
|
||
isErc1155, err := supportsInterface(receipt.ContractAddress, client, ERC1155InterfaceID) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if isErc1155 { | ||
result = append(result, adapters.Whitelist{ | ||
User: sender, | ||
BlockNumber: block.Number().Uint64(), | ||
Time: block.Time(), | ||
}) | ||
} | ||
} | ||
|
||
return result, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package conft | ||
|
||
var ( | ||
ABI = `[ | ||
{ | ||
"anonymous": false, | ||
"inputs": [ | ||
{ | ||
"indexed": false, | ||
"internalType": "uint128", | ||
"name": "id", | ||
"type": "uint128" | ||
}, | ||
{ | ||
"indexed": true, | ||
"internalType": "address", | ||
"name": "seller", | ||
"type": "address" | ||
}, | ||
{ | ||
"indexed": false, | ||
"internalType": "address", | ||
"name": "buyer", | ||
"type": "address" | ||
}, | ||
{ | ||
"indexed": true, | ||
"internalType": "address", | ||
"name": "contractAddress", | ||
"type": "address" | ||
}, | ||
{ | ||
"indexed": true, | ||
"internalType": "uint256", | ||
"name": "tokenId", | ||
"type": "uint256" | ||
}, | ||
{ | ||
"indexed": false, | ||
"internalType": "uint128", | ||
"name": "price", | ||
"type": "uint128" | ||
} | ||
], | ||
"name": "TokenSold", | ||
"type": "event" | ||
} | ||
]` | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package erc165 | ||
|
||
var ( | ||
ABI = `[ | ||
{ | ||
"constant": true, | ||
"inputs": [ | ||
{ | ||
"name": "interfaceId", | ||
"type": "bytes4" | ||
} | ||
], | ||
"name": "supportsInterface", | ||
"outputs": [ | ||
{ | ||
"name": "", | ||
"type": "bool" | ||
} | ||
], | ||
"payable": false, | ||
"stateMutability": "view", | ||
"type": "function" | ||
} | ||
]` | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package conft | ||
|
||
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/conft" | ||
) | ||
|
||
var ( | ||
logTokenSoldSigHash = crypto.Keccak256Hash([]byte("TokenSold(uint128,address,address,address,uint256,uint128)")) | ||
) | ||
|
||
type TokenSoldEvent struct { | ||
ID *big.Int | ||
Seller common.Address | ||
Buyer common.Address | ||
ContractAddress common.Address | ||
TokenID *big.Int | ||
Price *big.Int | ||
} | ||
|
||
type TokenSoldIndexer struct { | ||
TargetAddresses []common.Address | ||
} | ||
|
||
// NewTokenSoldIndexer creates a new TokenSoldIndexer. | ||
func NewTokenSoldIndexer() *TokenSoldIndexer { | ||
return &TokenSoldIndexer{TargetAddresses: []common.Address{common.HexToAddress("0x6Ce2CFD7674cf47A851690a11d1DB45c6cCBe17A")}} | ||
} | ||
|
||
func (indexer *TokenSoldIndexer) Addresses() []common.Address { | ||
return indexer.TargetAddresses | ||
} | ||
|
||
func (indexer *TokenSoldIndexer) IndexLogs(ctx context.Context, chainID *big.Int, client *ethclient.Client, logs []types.Log) ([]adapters.Whitelist, error) { | ||
var result []adapters.Whitelist | ||
for _, vLog := range logs { | ||
if !indexer.isRelevantLog(vLog.Topics[0]) { | ||
continue | ||
} | ||
transferData, err := indexer.ProcessLog(ctx, chainID, client, vLog) | ||
if err != nil { | ||
return nil, err | ||
} | ||
result = append(result, *transferData) | ||
} | ||
return result, nil | ||
} | ||
|
||
func (indexer *TokenSoldIndexer) isRelevantLog(topic common.Hash) bool { | ||
return topic.Hex() == logTokenSoldSigHash.Hex() | ||
} | ||
|
||
func (indexer *TokenSoldIndexer) ProcessLog(ctx context.Context, chainID *big.Int, client *ethclient.Client, vLog types.Log) (*adapters.Whitelist, error) { | ||
var tokenSoldEvent TokenSoldEvent | ||
|
||
tokenSoldABI, err := abi.JSON(strings.NewReader(conft.ABI)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
err = tokenSoldABI.UnpackIntoInterface(&tokenSoldEvent, "OrderFulfilled", vLog.Data) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
block, err := client.BlockByNumber(ctx, big.NewInt(int64(vLog.BlockNumber))) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &adapters.Whitelist{ | ||
User: tokenSoldEvent.Buyer, | ||
Time: block.Time(), | ||
BlockNumber: block.Number().Uint64(), | ||
}, nil | ||
} |
Oops, something went wrong.