diff --git a/package.json b/package.json index c91fb1c..5f4bc6d 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "dependencies": { "@walletconnect/web3wallet": "^1.13.0", "elliptic": "^6.5.6", - "near-api-js": "^5.0.0", + "near-api-js": "^5.0.1", "viem": "^2.21.37" } } diff --git a/src/mpcContract.ts b/src/mpcContract.ts index f8de388..a4ea563 100644 --- a/src/mpcContract.ts +++ b/src/mpcContract.ts @@ -1,4 +1,4 @@ -import { Contract, Account } from "near-api-js"; +import { Contract, Account, transactions } from "near-api-js"; import { Address, Signature } from "viem"; import { deriveChildPublicKey, @@ -7,7 +7,8 @@ import { } from "./utils/kdf"; import { TGAS } from "./chains/near"; import { MPCSignature, FunctionCallTransaction, SignArgs } from "./types"; -import { transformSignature } from "./utils/signature"; +import { signatureFromOutcome } from "./utils/signature"; +import { FinalExecutionOutcome } from "near-api-js/lib/providers"; /** * Near Contract Type for change methods. @@ -107,14 +108,16 @@ export class MpcContract implements IMpcContract { signArgs: SignArgs, gas?: bigint ): Promise => { - const mpcSig = await this.contract.sign({ - signerAccount: this.connectedAccount, - args: { request: signArgs }, - gas: gasOrDefault(gas), - amount: await this.getDeposit(), - }); - - return transformSignature(mpcSig); + // near-api-js SUX so bad we can't configure this RPC timeout. + // const mpcSig = await this.contract.sign({ + // signerAccount: this.connectedAccount, + // args: { request: signArgs }, + // gas: gasOrDefault(gas), + // amount: await this.getDeposit(), + // }); + const transaction = await this.encodeSignatureRequestTx(signArgs, gas); + const outcome = await this.signAndSendSignRequest(transaction); + return signatureFromOutcome(outcome); }; async encodeSignatureRequestTx( @@ -137,6 +140,41 @@ export class MpcContract implements IMpcContract { ], }; } + + async signAndSendSignRequest( + transaction: FunctionCallTransaction<{ request: SignArgs }>, + blockTimeout: number = 30 + ): Promise { + const account = this.connectedAccount; + // @ts-expect-error: Account.signTransaction is protected (for no apparantly good reason) + const [txHash, signedTx] = await account.signTransaction( + this.contract.contractId, + transaction.actions.map(({ params: { args, gas, deposit } }) => + transactions.functionCall("sign", args, BigInt(gas), BigInt(deposit)) + ) + ); + const provider = account.connection.provider; + let outcome = await provider.sendTransactionAsync(signedTx); + + let pings = 0; + while ( + outcome.final_execution_status != "EXECUTED" && + pings < blockTimeout + ) { + // Sleep 1 second before next ping. + await new Promise((resolve) => setTimeout(resolve, 1000)); + // txStatus times out when waiting for 'EXECUTED'. + // Instead we wait for an earlier status type, sleep between and keep pinging. + outcome = await provider.txStatus(txHash, account.accountId, "INCLUDED"); + pings += 1; + } + if (pings >= blockTimeout) { + console.warn( + `Request status polling exited before desired outcome.\n Current status: ${outcome.final_execution_status}\nSignature Request will likley fail.` + ); + } + return outcome; + } } function gasOrDefault(gas?: bigint): string { diff --git a/tests/e2e.test.ts b/tests/e2e.test.ts index 49f9dad..c778d32 100644 --- a/tests/e2e.test.ts +++ b/tests/e2e.test.ts @@ -60,7 +60,7 @@ describe("End To End", () => { chainId, }) ).rejects.toThrow(); - }); + }, 15000); it("signMessage", async () => { const message = "NearEth"; diff --git a/yarn.lock b/yarn.lock index ca6017a..a2f1acb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3794,7 +3794,7 @@ near-abi@0.1.1: dependencies: "@types/json-schema" "^7.0.11" -near-api-js@^5.0.0: +near-api-js@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/near-api-js/-/near-api-js-5.0.1.tgz#a393547cabeeb7a8a445a4d786ec3aef948e0d84" integrity sha512-ZSQYIQdekIvSRfOFuRj6MW11jtK5lsOaiWM2VB0nq7eROuuxwSSXHg+syjCXl3HNNZ3hzg0CNdp+5Za0X1ZtYg==