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

implement tx.wait() #645

Merged
merged 6 commits into from
Dec 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- `Circuit.constraintSystemFromKeypair(keypair)` to inspect the circuit at a low level https://github.com/o1-labs/snarkyjs/pull/529
- Works with a `keypair` (prover + verifier key) generated with the `Circuit` API
- `tx.wait()` is now implemented. It waits for the transactions inclusion in a block https://github.com/o1-labs/snarkyjs/pull/645
- `wait()` also now takes an optional `options` parameter to specify the polling interval or maximum attempts. `wait(options?: { maxAttempts?: number; interval?: number }): Promise<void>;`

### Changed

Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ export {
export {
fetchAccount,
fetchLastBlock,
fetchTransactionStatus,
TransactionStatus,
addCachedAccount,
setGraphqlEndpoint,
sendZkapp,
Expand Down
35 changes: 35 additions & 0 deletions src/lib/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export {
markAccountToBeFetched,
markNetworkToBeFetched,
fetchMissingData,
fetchTransactionStatus,
TransactionStatus,
getCachedAccount,
getCachedNetwork,
addCachedAccount,
Expand Down Expand Up @@ -534,6 +536,39 @@ function parseEpochData({
};
}

const transactionStatusQuery = (txId: string) => `query {
transactionStatus(zkappTransaction:"${txId}")
}`;

/**
* Fetches the status of a transaction.
*/
async function fetchTransactionStatus(
txId: string,
graphqlEndpoint = defaultGraphqlEndpoint
): Promise<TransactionStatus> {
let [resp, error] = await makeGraphqlRequest(
transactionStatusQuery(txId),
graphqlEndpoint
);
if (error) throw Error(error.statusText);
let txStatus = resp?.data?.transactionStatus;
if (txStatus === undefined || txStatus === null) {
throw Error(`Failed to fetch transaction status. TransactionId: ${txId}`);
}
return txStatus as TransactionStatus;
}

/**
* INCLUDES: A transaction that is on the longest chain
*
* PENDING: A transaction either in the transition frontier or in transaction pool but is not on the longest chain
*
* UNKNOWN: The transaction has either been snarked, reached finality through consensus or has been dropped
*
*/
type TransactionStatus = 'INCLUDED' | 'PENDING' | 'UNKNOWN';

/**
* Sends a zkApp command (transaction) to the specified GraphQL endpoint.
*/
Expand Down
49 changes: 42 additions & 7 deletions src/lib/mina.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export {
filterGroups,
};
interface TransactionId {
wait(): Promise<void>;
wait(options?: { maxAttempts?: number; interval?: number }): Promise<void>;
hash(): string;
}

Expand Down Expand Up @@ -475,10 +475,17 @@ function LocalBlockchain({
}
});
return {
wait: async () => {},
wait: async (_options?: {
maxAttempts?: number;
interval?: number;
}) => {
console.log(
'Info: Waiting for inclusion in a block is not supported for LocalBlockchain.'
);
},
hash: (): string => {
const message =
'Txn Hash retrieving is not supported for LocalBlockchain.';
'Info: Txn Hash retrieving is not supported for LocalBlockchain.';
console.log(message);
return message;
},
Expand Down Expand Up @@ -642,13 +649,41 @@ function Network(graphqlEndpoint: string): Mina {
errors = [error];
}

let maxAttempts: number;
let attempts = 0;
let interval: number;

return {
data: response?.data,
errors,
async wait() {
console.log(
'Info: waiting for inclusion in a block is not implemented yet.'
);
async wait(options?: { maxAttempts?: number; interval?: number }) {
// default is 45 attempts * 20s each = 15min
// the block time on berkeley is currently longer than the average 3-4min, so its better to target a higher block time
// fetching an update every 20s is more than enough with a current block time of 3min
maxAttempts = options?.maxAttempts ?? 45;
interval = options?.interval ?? 20000;

const executePoll = async (
resolve: () => void,
reject: (err: Error) => void | Error
) => {
let txId = response?.data?.sendZkapp?.zkapp?.id;
let res = await Fetch.fetchTransactionStatus(txId);
attempts++;
if (res === 'INCLUDED') {
return resolve();
} else if (maxAttempts && attempts === maxAttempts) {
return reject(
new Error(
`Exceeded max attempts. TransactionId: ${txId}, attempts: ${attempts}`
)
);
} else {
setTimeout(executePoll, interval, resolve, reject);
}
};

return new Promise(executePoll);
},
hash() {
return response?.data?.sendZkapp?.zkapp?.hash;
Expand Down