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

[Doc] How to get a list of transactions? #20674

Open
mahnunchik opened this issue Dec 18, 2024 · 3 comments
Open

[Doc] How to get a list of transactions? #20674

mahnunchik opened this issue Dec 18, 2024 · 3 comments
Assignees
Labels
doc-issue Issue submitted using the Doc issue template ts-sdk

Comments

@mahnunchik
Copy link

The question: How to get a list of transactions by address? I mean incoming and outgoing transactions.

This simple and logical question is not answered either in the common documentation https://docs.sui.io/ or in the SDK documentation https://sdk.mystenlabs.com/typescript


There is documentation how to work with keys and addresses: https://docs.sui.io/concepts/cryptography/transaction-auth/keys-addresses

const keypair = Ed25519Keypair.deriveKeypair(TEST_MNEMONIC, `m/44'/784'/0'/0'/0'`);
const address = keypair.getPublicKey().toSuiAddress();

There is sample of code how to get balance: https://sdk.mystenlabs.com/typescript/hello-sui

const suiAfter = await suiClient.getBalance({
	owner: MY_ADDRESS,
});

But how to get a list of transactions? Could be provided in the documentation an example of a request to RPC? Or an example of queries to GraphQL?

@mahnunchik mahnunchik added the doc-issue Issue submitted using the Doc issue template label Dec 18, 2024
@mahnunchik
Copy link
Author

Update: how to get list of all transactions from and to address.

It seems code from Sui Wallet loads only QUERY_MAX_RESULT_LIMIT = 50 incoming and 50 outgoing transactions

const [txnIds, fromTxnIds] = await Promise.all([
rpc.queryTransactionBlocks({
filter: {
ToAddress: address!,
},
options: {
showInput: true,
showEffects: true,
showEvents: true,
},
}),
rpc.queryTransactionBlocks({
filter: {
FromAddress: address!,
},
options: {
showInput: true,
showEffects: true,
showEvents: true,
},
}),
]);

@nixjs
Copy link

nixjs commented Dec 22, 2024

export async function getTransactionsForAddress(
        network: 'mainnet' | 'testnet' | 'devnet' | 'localnet',
        address: PrimitiveHexString,
        limit?: number
    ): Promise<TransactionTypes.Transaction[]> {
        const client = new SuiClient({ url: getFullnodeUrl(network) }) // or 'testnet' or 'devnet'
        const senderTx = await client.queryTransactionBlocks({
            filter: {
                FromAddress: address,
            },
            options: {
                showInput: true,
                showEvents: true,
                showObjectChanges: true,
                showBalanceChanges: true,
            },
            limit: limit ?? 1000, // Adjust this based on how many transactions you want to fetch per query
        })
        const recipientTx = await client.queryTransactionBlocks({
            filter: {
                ToAddress: address,
            },
            options: {
                showInput: true,
                showEvents: true,
                showObjectChanges: true,
                showBalanceChanges: true,
                showEffects: true, // To get transaction status and gas fee details
            },
            limit: limit ?? 1000, // Adjust this based on how many transactions you want to fetch per query
        })
        const txn = [...senderTx.data, ...recipientTx.data].sort((a, b) => Number(b.timestampMs) - Number(a.timestampMs))

        const transactions = txn.map<TransactionTypes.Transaction>((tx) => {
            // Assuming 'tx' is the transaction block
            const { digest, timestampMs, effects, transaction } = tx
            const balanceChanges = tx.balanceChanges || []
            const gasFee = BigNumber(tx.effects?.gasUsed.computationCost || 0)
                .dividedBy(1_000_000_000)
                .toFixed() // Gas fee in MIST
            let recipient = 'Unknown'
            let sender = tx.transaction?.data?.sender ?? 'Unknown'
            sender = tx.transaction?.data?.sender === address ? address : sender

            let isSender = sender === address
            let isReceiver = false
            let type: TransactionEnums.TransactionType = TransactionEnums.TransactionType.NONE

            balanceChanges.forEach((change) => {
                const owner = change.owner as any
                if (owner?.AddressOwner && owner.AddressOwner !== address) {
                    recipient = owner.AddressOwner
                }
                if (owner?.AddressOwner === address) {
                    if (Number(change.amount) > 0) {
                        isReceiver = true // Receiving SUI
                    }
                    if (Number(change.amount) < 0) {
                        // Sending SUI
                        isSender = true // This confirms it's a sender if we haven't already
                    }
                } else if (owner?.AddressOwner) {
                    recipient = owner?.AddressOwner // First non-address change could be considered the recipient
                }
            })

            // Classify transaction type based on whether it's sending, receiving, or both
            if (isSender && isReceiver) {
                type = TransactionEnums.TransactionType.MIXED
            } else if (isSender) {
                type = TransactionEnums.TransactionType.SEND
            } else if (isReceiver) {
                type = TransactionEnums.TransactionType.RECEIVE
            }

            return {
                timestamp: timestampMs ? Math.floor(Number(timestampMs) / 1000) : null,
                status:
                    effects?.status.status === 'success'
                        ? TransactionEnums.TransactionStatus.SUCCESS
                        : TransactionEnums.TransactionStatus.FAILED,
                hash: digest,
                gasFee,
                from: sender,
                to: recipient,
                data: {
                    overview: digest,
                    transaction,
                } as TransactionTypes.ScriptObject,
                type,
            }
        })

        return transactions
    }

@mahnunchik
Copy link
Author

@nixjs

limit: limit ?? 1000, // Adjust this based on how many transactions you want to fetch per query

The limit cannot be more than 50

Your example loads no more than 50 incoming and no more than 50 outgoing transactions. To show transactions in the correct order by date, it is required to load all incoming and all outgoing, i.e. pagination is necessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
doc-issue Issue submitted using the Doc issue template ts-sdk
Projects
None yet
Development

No branches or pull requests

4 participants