diff --git a/CHANGELOG.md b/CHANGELOG.md index 51ebddf75..bf1bce1a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to the Aptos TypeScript SDK will be captured in this file. T - Respect `API_KEY` option in `clientConfig` when making indexer and/or fullnode queries - [`Added`] Added `waitForIndexer` function to wait for indexer to sync up with full node. All indexer query functions now accepts a new optional param `minimumLedgerVersion` to wait for indexer to sync up with the target processor. +- Add `getSigningMessage` to allow users to sign transactions with external signers and other use cases Breaking: - Changes ANS date usage to consistently use epoch timestamps represented in milliseconds. diff --git a/examples/typescript/external_signing.ts b/examples/typescript/external_signing.ts new file mode 100644 index 000000000..60ef2d185 --- /dev/null +++ b/examples/typescript/external_signing.ts @@ -0,0 +1,199 @@ +/* eslint-disable no-console */ + +/** + * This example shows an example of how one might send transactions elsewhere to be signed outside the SDK. + */ + +import { + Account, + AccountAddress, + AccountAuthenticator, + AccountAuthenticatorEd25519, + Aptos, + AptosConfig, + Deserializer, + Ed25519PrivateKey, + Ed25519PublicKey, + Ed25519Signature, + Network, + NetworkToNetworkName, + RawTransaction, + Serializer, +} from "@aptos-labs/ts-sdk"; +import nacl from "tweetnacl"; + +const APTOS_COIN = "0x1::aptos_coin::AptosCoin"; +const COIN_STORE = "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>"; +const COLD_INITIAL_BALANCE = 100_000_000; +const HOT_INITIAL_BALANCE = 100; +const TRANSFER_AMOUNT = 100; + +// Default to devnet, but allow for overriding +const APTOS_NETWORK: Network = NetworkToNetworkName[process.env.APTOS_NETWORK] || Network.DEVNET; + +const balance = async (aptos: Aptos, account: Account, name: string): Promise => { + type Coin = { coin: { value: string } }; + const resource = await aptos.getAccountResource({ + accountAddress: account.accountAddress, + resourceType: COIN_STORE, + }); + const amount = Number(resource.coin.value); + + console.log(`${name}'s balance is: ${amount}`); + return amount; +}; + +/** + * Provides a mock "Cold wallet" that's signed externally from the SDK + */ +class ExternalSigner { + private account: Account; + + private aptos: Aptos; + + public name: string; + + public initialBalance: number; + + public isSetup: boolean; + + private extractedPrivateKey: nacl.SignKeyPair; + + constructor(name: string, initialBalance: number) { + const config = new AptosConfig({ network: APTOS_NETWORK }); + this.aptos = new Aptos(config); + this.account = Account.generate(); + this.name = name; + this.initialBalance = initialBalance; + this.isSetup = false; + this.extractedPrivateKey = nacl.sign.keyPair.fromSeed( + this.account.privateKey.toUint8Array().slice(0, Ed25519PrivateKey.LENGTH), + ); + } + + address(): AccountAddress { + return this.account.accountAddress; + } + + /** + * Setup the account making sure it has funds and exists + */ + async setup() { + if (this.isSetup) { + throw new Error(`Tried to double setup ${this.name}`); + } + + console.log(`${this.name}'s address is: ${this.account.accountAddress}`); + + const fundTxn = await this.aptos.fundAccount({ + accountAddress: this.account.accountAddress, + amount: this.initialBalance, + }); + console.log(`${this.name}'s fund transaction: `, fundTxn); + this.isSetup = true; + } + + async balance(): Promise { + return balance(this.aptos, this.account, this.name); + } + + /** + * Pretends to sign from a cold wallet + * @param encodedTransaction an already encoded signing message + */ + sign(encodedTransaction: Uint8Array): Uint8Array { + // Sending the full transaction as BCS encoded, allows for full text viewing of the transaction on the signer. + // However, this is not required, and the signer could just send the signing message. + const deserializer = new Deserializer(encodedTransaction); + const rawTransaction = RawTransaction.deserialize(deserializer); + + // Some changes to make it signable, this would need more logic for fee payer or additional signers + // TODO: Make BCS handle any object type? + const transaction = { rawTransaction }; + const signingMessage = this.aptos.getSigningMessage({ transaction }); + + // Pretend that it's an external signer that only knows bytes using a raw crypto library + const signature = nacl.sign.detached(signingMessage, this.extractedPrivateKey.secretKey); + + // Construct the authenticator with the public key for the submission + const authenticator = new AccountAuthenticatorEd25519( + this.account.publicKey as Ed25519PublicKey, + new Ed25519Signature(signature), + ); + + const serializer = new Serializer(); + authenticator.serialize(serializer); + return serializer.toUint8Array(); + } +} + +const example = async () => { + console.log("This example will pretend that hot is on a separate server, and never access information from it"); + + // Setup the client + const config = new AptosConfig({ network: APTOS_NETWORK }); + const aptos = new Aptos(config); + + // Create two accounts + const cold = new ExternalSigner("Cold", COLD_INITIAL_BALANCE); + const hot = Account.generate(); + await aptos.fundAccount({ accountAddress: hot.accountAddress, amount: HOT_INITIAL_BALANCE }); + + console.log("\n=== Funding accounts ===\n"); + await cold.setup(); + + // Show the balances + console.log("\n=== Balances ===\n"); + const coldBalance = await cold.balance(); + const hotBalance = await balance(aptos, hot, "Hot"); + + if (coldBalance !== COLD_INITIAL_BALANCE) throw new Error("Cold's balance is incorrect"); + if (hotBalance !== HOT_INITIAL_BALANCE) throw new Error("Hot's balance is incorrect"); + + // Transfer between users + const singleSignerTransaction = await aptos.build.transaction({ + sender: cold.address(), + data: { + function: "0x1::coin::transfer", + typeArguments: [APTOS_COIN], + functionArguments: [hot.accountAddress, TRANSFER_AMOUNT], + }, + }); + + // Send the transaction to external signer to sign + const serializer = new Serializer(); + singleSignerTransaction.rawTransaction.serialize(serializer); + const rawTransactionBytes = serializer.toUint8Array(); + + // We're going to pretend that the network call is just an external function call + console.log("\n=== Signing ===\n"); + const authenticatorBytes = cold.sign(rawTransactionBytes); + const deserializer = new Deserializer(authenticatorBytes); + const authenticator = AccountAuthenticator.deserialize(deserializer); + + console.log(`Retrieved authenticator: ${JSON.stringify(authenticator)}`); + + // Combine the transaction and send + console.log("\n=== Transfer transaction ===\n"); + const committedTxn = await aptos.submit.transaction({ + transaction: singleSignerTransaction, + senderAuthenticator: authenticator, + }); + + await aptos.waitForTransaction({ transactionHash: committedTxn.hash }); + console.log(`Committed transaction: ${committedTxn.hash}`); + + console.log("\n=== Balances after transfer ===\n"); + const newColdBalance = await cold.balance(); + const newHotBalance = await balance(aptos, hot, "Hot"); + + // Hot should have the transfer amount + if (newHotBalance !== TRANSFER_AMOUNT + HOT_INITIAL_BALANCE) + throw new Error("Hot's balance after transfer is incorrect"); + + // Cold should have the remainder minus gas + if (newColdBalance >= COLD_INITIAL_BALANCE - TRANSFER_AMOUNT) + throw new Error("Cold's balance after transfer is incorrect"); +}; + +example(); diff --git a/examples/typescript/facoin/facoin.json b/examples/typescript/facoin/facoin.json index 1b13854a4..4560b2448 100644 --- a/examples/typescript/facoin/facoin.json +++ b/examples/typescript/facoin/facoin.json @@ -9,8 +9,8 @@ { "type": "hex", "value": [ - "0xa11ceb0b060000000c0100100210300340a60104e6011605fc01de0207da04ac0508860a4006c60a7710bd0ba1010ade0c0c0cea0cad040d9711060000010101020103010401050106010700080800020a0000030d07010001020e08000216060002180600021a0600021b080003270200042907010000072b07000009000100000b020100000c030100000f01040000100501000011000100001206010000130301000014070800061c050a00031d0c0d0108011e0e0e00031f0f0a01080520101101080221130101080522101101080223150101080224170101080325180a0003260a19010803281b1c00042a011e0100072c1f2000052d210100022e222300022f22240002302225000331222600021128080002322a01010802332c0801080a0b0c0b0d0b0e120f0b10121112130b151d1d121e1203060c05030003060c05080102060c05010b0201080301060c04060c05050303060c0305010801050b020108030b020108030608060b02010807060c0105010803020b02010900050101010301060b0201090002050b02010900010b02010807010807030608060b0201090003050b020108030b02010803060c0b02010807060805030608050b020109000801050b020108030b02010803060c0608050b02010807030608050b02010900010206050a02010b0201090006080808060608080c0804080502060c0a020108080104010b09010900010a0201080a070608080b090104080a080a02080a080a01060808010804010806010805010c060b020108030b020108030801060800060c0b020108070206080403060b020108030b020108030b02010807060c0b02010807060805040608050b020109000b0201090003050b020108030b020108030b02010807060c060805030608050b020109000303636174056572726f720e66756e6769626c655f6173736574066f626a656374066f7074696f6e167072696d6172795f66756e6769626c655f73746f7265067369676e657206737472696e67144d616e6167656446756e6769626c654173736574046275726e0d46756e6769626c654173736574076465706f7369740e667265657a655f6163636f756e74064f626a656374084d657461646174610c6765745f6d657461646174610b696e69745f6d6f64756c65046d696e74087472616e7366657210756e667265657a655f6163636f756e74087769746864726177086d696e745f726566074d696e745265660c7472616e736665725f7265660b5472616e73666572526566086275726e5f726566074275726e5265660d46756e6769626c6553746f72650a616464726573735f6f660869735f6f776e6572117065726d697373696f6e5f64656e6965640e6f626a6563745f616464726573730d7072696d6172795f73746f7265096275726e5f66726f6d1b656e737572655f7072696d6172795f73746f72655f657869737473106465706f7369745f776974685f7265660f7365745f66726f7a656e5f666c6167156372656174655f6f626a6563745f6164647265737311616464726573735f746f5f6f626a6563740e436f6e7374727563746f72526566136372656174655f6e616d65645f6f626a656374064f7074696f6e046e6f6e6506537472696e6704757466382b6372656174655f7072696d6172795f73746f72655f656e61626c65645f66756e6769626c655f61737365741167656e65726174655f6d696e745f7265661167656e65726174655f6275726e5f7265661567656e65726174655f7472616e736665725f7265660f67656e65726174655f7369676e6572117472616e736665725f776974685f7265661177697468647261775f776974685f7265660d610fd0f4a53e788c5c5c6756cc363cb854a82321e2ea4c948d061dd278243d00000000000000000000000000000000000000000000000000000000000000010a0204034341540308010000000000000005200d610fd0f4a53e788c5c5c6756cc363cb854a82321e2ea4c948d061dd278243d0a02090843415420436f696e0a021f1e687474703a2f2f6578616d706c652e636f6d2f66617669636f6e2e69636f0a021312687474703a2f2f6578616d706c652e636f6d126170746f733a3a6d657461646174615f76318c010101000000000000000a454e4f545f4f574e4552344f6e6c792066756e6769626c65206173736574206d65746164617461206f776e65722063616e206d616b65206368616e6765732e01144d616e6167656446756e6769626c654173736574010301183078313a3a6f626a6563743a3a4f626a65637447726f7570010c6765745f6d657461646174610101000002031508041708051908060001040100091d11030c030b000a030c040c070a040b0711093800040c050f0701110b270e0438012b0010000c050b010b0338020c060b050b060b023803020101000100141d11030c030b000a030c040c050a040b0511093800040c050f0701110b270e0438012b0010010c070b010b0338040c060b070b060b023805020201040100161d11030c020b000a020c030c040a030b0411093800040c050f0701110b270e0338012b0010010c050b010b0238040c060b050b0608380602030100000a0707020c000e0007001112380702040000001a250b00070011140c010e010c030a03380807031116070011163108070411160705111611170a0311180c050a0311190c020a03111a0c060b03111b0c040e040b050b060b0212002d00020501040100272211030c030b000a030c040c070a040b0711093800040c050f0701110b270e0438012b000c060b010b0338040c080a0610020b02111c0c050b0610010b080b053805020601040100292211030c040b000a040c050c070a050b0711093800040c050f0701110b270e0538012b0010010c090b010a0438020c060b020b0438040c080b090b060b080b033809020701040100161d11030c020b000a020c030c040a030b0411093800040c050f0701110b270e0338012b0010010c050b010b0238040c060b050b060938060208010001002b1d11030c030b000a030c040c060a040b0611093800040c050f0701110b270e0438012b0010010c070b020b0338020c050b070b050b01380a0200020001000000", - "0xa11ceb0b060000000c0100100210300340a60104e6011605fc01de0207da04ac0508860a4006c60a7710bd0ba1010ade0c0c0cea0cad040d9711060000010101020103010401050106010700080800020a0000030d07010001020e08000216060002180600021a0600021b080003270200042907010000072b07000009000100000b020100000c030100000f01040000100501000011000100001206010000130301000014070800061c050a00031d0c0d0108011e0e0e00031f0f0a01080520101101080221130101080522101101080223150101080224170101080325180a0003260a19010803281b1c00042a011e0100072c1f2000052d210100022e222300022f22240002302225000331222600021128080002322a01010802332c0801080a0b0c0b0d0b0e120f0b10121112130b151d1d121e1203060c05030003060c05080102060c05010b0201080301060c04060c05050303060c0305010801050b020108030b020108030608060b02010807060c0105010803020b02010900050101010301060b0201090002050b02010900010b02010807010807030608060b0201090003050b020108030b02010803060c0b02010807060805030608050b020109000801050b020108030b02010803060c0608050b02010807030608050b02010900010206050a02010b0201090006080808060608080c0804080502060c0a020108080104010b09010900010a0201080a070608080b090104080a080a02080a080a01060808010804010806010805010c060b020108030b020108030801060800060c0b020108070206080403060b020108030b020108030b02010807060c0b02010807060805040608050b020109000b0201090003050b020108030b020108030b02010807060c060805030608050b020109000303646f67056572726f720e66756e6769626c655f6173736574066f626a656374066f7074696f6e167072696d6172795f66756e6769626c655f73746f7265067369676e657206737472696e67144d616e6167656446756e6769626c654173736574046275726e0d46756e6769626c654173736574076465706f7369740e667265657a655f6163636f756e74064f626a656374084d657461646174610c6765745f6d657461646174610b696e69745f6d6f64756c65046d696e74087472616e7366657210756e667265657a655f6163636f756e74087769746864726177086d696e745f726566074d696e745265660c7472616e736665725f7265660b5472616e73666572526566086275726e5f726566074275726e5265660d46756e6769626c6553746f72650a616464726573735f6f660869735f6f776e6572117065726d697373696f6e5f64656e6965640e6f626a6563745f616464726573730d7072696d6172795f73746f7265096275726e5f66726f6d1b656e737572655f7072696d6172795f73746f72655f657869737473106465706f7369745f776974685f7265660f7365745f66726f7a656e5f666c6167156372656174655f6f626a6563745f6164647265737311616464726573735f746f5f6f626a6563740e436f6e7374727563746f72526566136372656174655f6e616d65645f6f626a656374064f7074696f6e046e6f6e6506537472696e6704757466382b6372656174655f7072696d6172795f73746f72655f656e61626c65645f66756e6769626c655f61737365741167656e65726174655f6d696e745f7265661167656e65726174655f6275726e5f7265661567656e65726174655f7472616e736665725f7265660f67656e65726174655f7369676e6572117472616e736665725f776974685f7265661177697468647261775f776974685f7265660d610fd0f4a53e788c5c5c6756cc363cb854a82321e2ea4c948d061dd278243d00000000000000000000000000000000000000000000000000000000000000010a020403444f470308010000000000000005200d610fd0f4a53e788c5c5c6756cc363cb854a82321e2ea4c948d061dd278243d0a020908444f4720436f696e0a021f1e687474703a2f2f6578616d706c652e636f6d2f66617669636f6e2e69636f0a021312687474703a2f2f6578616d706c652e636f6d126170746f733a3a6d657461646174615f76318c010101000000000000000a454e4f545f4f574e4552344f6e6c792066756e6769626c65206173736574206d65746164617461206f776e65722063616e206d616b65206368616e6765732e01144d616e6167656446756e6769626c654173736574010301183078313a3a6f626a6563743a3a4f626a65637447726f7570010c6765745f6d657461646174610101000002031508041708051908060001040100091d11030c030b000a030c040c070a040b0711093800040c050f0701110b270e0438012b0010000c050b010b0338020c060b050b060b023803020101000100141d11030c030b000a030c040c050a040b0511093800040c050f0701110b270e0438012b0010010c070b010b0338040c060b070b060b023805020201040100161d11030c020b000a020c030c040a030b0411093800040c050f0701110b270e0338012b0010010c050b010b0238040c060b050b0608380602030100000a0707020c000e0007001112380702040000001a250b00070011140c010e010c030a03380807031116070011163108070411160705111611170a0311180c050a0311190c020a03111a0c060b03111b0c040e040b050b060b0212002d00020501040100272211030c030b000a030c040c070a040b0711093800040c050f0701110b270e0438012b000c060b010b0338040c080a0610020b02111c0c050b0610010b080b053805020601040100292211030c040b000a040c050c070a050b0711093800040c050f0701110b270e0538012b0010010c090b010a0438020c060b020b0438040c080b090b060b080b033809020701040100161d11030c020b000a020c030c040a030b0411093800040c050f0701110b270e0338012b0010010c050b010b0238040c060b050b060938060208010001002b1d11030c030b000a030c040c060a040b0611093800040c050f0701110b270e0438012b0010010c070b020b0338020c050b070b050b01380a0200020001000000" + "0xa11ceb0b060000000c0100100210300340a60104e6011605fc01de0207da04ac0508860a4006c60a7710bd0ba1010ade0c0c0cea0cad040d9711060000010101020103010401050106010700080800020a0000030d07010001020e08000216060002180600021a0600021b080003270200042907010000072b07000009000100000b020100000c030100000f01040000100501000011000100001206010000130301000014070800061c050a00031d0c0d0108011e0e0e00031f0f0a01080520101101080221130101080522101101080223150101080224170101080325180a0003260a19010803281b1c00042a011e0100072c1f2000052d210100022e222300022f22240002302225000331222600021128080002322a01010802332c0801080a0b0c0b0d0b0e120f0b10121112130b151d1d121e1203060c05030003060c05080102060c05010b0201080301060c04060c05050303060c0305010801050b020108030b020108030608060b02010807060c0105010803020b02010900050101010301060b0201090002050b02010900010b02010807010807030608060b0201090003050b020108030b02010803060c0b02010807060805030608050b020109000801050b020108030b02010803060c0608050b02010807030608050b02010900010206050a02010b0201090006080808060608080c0804080502060c0a020108080104010b09010900010a0201080a070608080b090104080a080a02080a080a01060808010804010806010805010c060b020108030b020108030801060800060c0b020108070206080403060b020108030b020108030b02010807060c0b02010807060805040608050b020109000b0201090003050b020108030b020108030b02010807060c060805030608050b020109000303636174056572726f720e66756e6769626c655f6173736574066f626a656374066f7074696f6e167072696d6172795f66756e6769626c655f73746f7265067369676e657206737472696e67144d616e6167656446756e6769626c654173736574046275726e0d46756e6769626c654173736574076465706f7369740e667265657a655f6163636f756e74064f626a656374084d657461646174610c6765745f6d657461646174610b696e69745f6d6f64756c65046d696e74087472616e7366657210756e667265657a655f6163636f756e74087769746864726177086d696e745f726566074d696e745265660c7472616e736665725f7265660b5472616e73666572526566086275726e5f726566074275726e5265660d46756e6769626c6553746f72650a616464726573735f6f660869735f6f776e6572117065726d697373696f6e5f64656e6965640e6f626a6563745f616464726573730d7072696d6172795f73746f7265096275726e5f66726f6d1b656e737572655f7072696d6172795f73746f72655f657869737473106465706f7369745f776974685f7265660f7365745f66726f7a656e5f666c6167156372656174655f6f626a6563745f6164647265737311616464726573735f746f5f6f626a6563740e436f6e7374727563746f72526566136372656174655f6e616d65645f6f626a656374064f7074696f6e046e6f6e6506537472696e6704757466382b6372656174655f7072696d6172795f73746f72655f656e61626c65645f66756e6769626c655f61737365741167656e65726174655f6d696e745f7265661167656e65726174655f6275726e5f7265661567656e65726174655f7472616e736665725f7265660f67656e65726174655f7369676e6572117472616e736665725f776974685f7265661177697468647261775f776974685f726566332f99c82f033d48b94869db8e32734d5770794058c8c816564a9c1871f0fe9a00000000000000000000000000000000000000000000000000000000000000010a020403434154030801000000000000000520332f99c82f033d48b94869db8e32734d5770794058c8c816564a9c1871f0fe9a0a02090843415420436f696e0a021f1e687474703a2f2f6578616d706c652e636f6d2f66617669636f6e2e69636f0a021312687474703a2f2f6578616d706c652e636f6d126170746f733a3a6d657461646174615f76318c010101000000000000000a454e4f545f4f574e4552344f6e6c792066756e6769626c65206173736574206d65746164617461206f776e65722063616e206d616b65206368616e6765732e01144d616e6167656446756e6769626c654173736574010301183078313a3a6f626a6563743a3a4f626a65637447726f7570010c6765745f6d657461646174610101000002031508041708051908060001040100091d11030c030b000a030c040c070a040b0711093800040c050f0701110b270e0438012b0010000c050b010b0338020c060b050b060b023803020101000100141d11030c030b000a030c040c050a040b0511093800040c050f0701110b270e0438012b0010010c070b010b0338040c060b070b060b023805020201040100161d11030c020b000a020c030c040a030b0411093800040c050f0701110b270e0338012b0010010c050b010b0238040c060b050b0608380602030100000a0707020c000e0007001112380702040000001a250b00070011140c010e010c030a03380807031116070011163108070411160705111611170a0311180c050a0311190c020a03111a0c060b03111b0c040e040b050b060b0212002d00020501040100272211030c030b000a030c040c070a040b0711093800040c050f0701110b270e0438012b000c060b010b0338040c080a0610020b02111c0c050b0610010b080b053805020601040100292211030c040b000a040c050c070a050b0711093800040c050f0701110b270e0538012b0010010c090b010a0438020c060b020b0438040c080b090b060b080b033809020701040100161d11030c020b000a020c030c040a030b0411093800040c050f0701110b270e0338012b0010010c050b010b0238040c060b050b060938060208010001002b1d11030c030b000a030c040c060a040b0611093800040c050f0701110b270e0438012b0010010c070b020b0338020c050b070b050b01380a0200020001000000", + "0xa11ceb0b060000000c0100100210300340a60104e6011605fc01de0207da04ac0508860a4006c60a7710bd0ba1010ade0c0c0cea0cad040d9711060000010101020103010401050106010700080800020a0000030d07010001020e08000216060002180600021a0600021b080003270200042907010000072b07000009000100000b020100000c030100000f01040000100501000011000100001206010000130301000014070800061c050a00031d0c0d0108011e0e0e00031f0f0a01080520101101080221130101080522101101080223150101080224170101080325180a0003260a19010803281b1c00042a011e0100072c1f2000052d210100022e222300022f22240002302225000331222600021128080002322a01010802332c0801080a0b0c0b0d0b0e120f0b10121112130b151d1d121e1203060c05030003060c05080102060c05010b0201080301060c04060c05050303060c0305010801050b020108030b020108030608060b02010807060c0105010803020b02010900050101010301060b0201090002050b02010900010b02010807010807030608060b0201090003050b020108030b02010803060c0b02010807060805030608050b020109000801050b020108030b02010803060c0608050b02010807030608050b02010900010206050a02010b0201090006080808060608080c0804080502060c0a020108080104010b09010900010a0201080a070608080b090104080a080a02080a080a01060808010804010806010805010c060b020108030b020108030801060800060c0b020108070206080403060b020108030b020108030b02010807060c0b02010807060805040608050b020109000b0201090003050b020108030b020108030b02010807060c060805030608050b020109000303646f67056572726f720e66756e6769626c655f6173736574066f626a656374066f7074696f6e167072696d6172795f66756e6769626c655f73746f7265067369676e657206737472696e67144d616e6167656446756e6769626c654173736574046275726e0d46756e6769626c654173736574076465706f7369740e667265657a655f6163636f756e74064f626a656374084d657461646174610c6765745f6d657461646174610b696e69745f6d6f64756c65046d696e74087472616e7366657210756e667265657a655f6163636f756e74087769746864726177086d696e745f726566074d696e745265660c7472616e736665725f7265660b5472616e73666572526566086275726e5f726566074275726e5265660d46756e6769626c6553746f72650a616464726573735f6f660869735f6f776e6572117065726d697373696f6e5f64656e6965640e6f626a6563745f616464726573730d7072696d6172795f73746f7265096275726e5f66726f6d1b656e737572655f7072696d6172795f73746f72655f657869737473106465706f7369745f776974685f7265660f7365745f66726f7a656e5f666c6167156372656174655f6f626a6563745f6164647265737311616464726573735f746f5f6f626a6563740e436f6e7374727563746f72526566136372656174655f6e616d65645f6f626a656374064f7074696f6e046e6f6e6506537472696e6704757466382b6372656174655f7072696d6172795f73746f72655f656e61626c65645f66756e6769626c655f61737365741167656e65726174655f6d696e745f7265661167656e65726174655f6275726e5f7265661567656e65726174655f7472616e736665725f7265660f67656e65726174655f7369676e6572117472616e736665725f776974685f7265661177697468647261775f776974685f726566332f99c82f033d48b94869db8e32734d5770794058c8c816564a9c1871f0fe9a00000000000000000000000000000000000000000000000000000000000000010a020403444f47030801000000000000000520332f99c82f033d48b94869db8e32734d5770794058c8c816564a9c1871f0fe9a0a020908444f4720436f696e0a021f1e687474703a2f2f6578616d706c652e636f6d2f66617669636f6e2e69636f0a021312687474703a2f2f6578616d706c652e636f6d126170746f733a3a6d657461646174615f76318c010101000000000000000a454e4f545f4f574e4552344f6e6c792066756e6769626c65206173736574206d65746164617461206f776e65722063616e206d616b65206368616e6765732e01144d616e6167656446756e6769626c654173736574010301183078313a3a6f626a6563743a3a4f626a65637447726f7570010c6765745f6d657461646174610101000002031508041708051908060001040100091d11030c030b000a030c040c070a040b0711093800040c050f0701110b270e0438012b0010000c050b010b0338020c060b050b060b023803020101000100141d11030c030b000a030c040c050a040b0511093800040c050f0701110b270e0438012b0010010c070b010b0338040c060b070b060b023805020201040100161d11030c020b000a020c030c040a030b0411093800040c050f0701110b270e0338012b0010010c050b010b0238040c060b050b0608380602030100000a0707020c000e0007001112380702040000001a250b00070011140c010e010c030a03380807031116070011163108070411160705111611170a0311180c050a0311190c020a03111a0c060b03111b0c040e040b050b060b0212002d00020501040100272211030c030b000a030c040c070a040b0711093800040c050f0701110b270e0438012b000c060b010b0338040c080a0610020b02111c0c050b0610010b080b053805020601040100292211030c040b000a040c050c070a050b0711093800040c050f0701110b270e0538012b0010010c090b010a0438020c060b020b0438040c080b090b060b080b033809020701040100161d11030c020b000a020c030c040a030b0411093800040c050f0701110b270e0338012b0010010c050b010b0238040c060b050b060938060208010001002b1d11030c030b000a030c040c060a040b0611093800040c050f0701110b270e0438012b0010010c070b020b0338020c050b070b050b01380a0200020001000000" ] } ] diff --git a/examples/typescript/package.json b/examples/typescript/package.json index 178be1a86..968db9dc4 100644 --- a/examples/typescript/package.json +++ b/examples/typescript/package.json @@ -12,7 +12,8 @@ "custom_client": "ts-node custom_client.ts", "swap": "ts-node swap.ts", "publish_package_from_filepath": "ts-node publish_package_from_filepath.ts", - "test": "run-s simple_transfer mint_nft multi_agent_transfer simple_sponsored_transaction transfer_coin custom_client publish_package_from_filepath" + "external_signing": "ts-node external_signing.ts", + "test": "run-s simple_transfer mint_nft multi_agent_transfer simple_sponsored_transaction transfer_coin custom_client publish_package_from_filepath external_signing" }, "keywords": [], "author": "", @@ -20,7 +21,8 @@ "dependencies": { "dotenv": "^16.3.1", "npm-run-all": "latest", - "superagent": "^8.1.2" + "superagent": "^8.1.2", + "tweetnacl": "^1.0.3" }, "peerDependencies": { "@aptos-labs/ts-sdk": "link:../.." diff --git a/examples/typescript/pnpm-lock.yaml b/examples/typescript/pnpm-lock.yaml index f62515ee8..3f495159e 100644 --- a/examples/typescript/pnpm-lock.yaml +++ b/examples/typescript/pnpm-lock.yaml @@ -17,6 +17,9 @@ dependencies: superagent: specifier: ^8.1.2 version: 8.1.2 + tweetnacl: + specifier: ^1.0.3 + version: 1.0.3 devDependencies: '@types/node': @@ -967,6 +970,10 @@ packages: yn: 3.1.1 dev: true + /tweetnacl@1.0.3: + resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} + dev: false + /typed-array-buffer@1.0.0: resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} engines: {node: '>= 0.4'} diff --git a/src/api/transaction.ts b/src/api/transaction.ts index 5405e00c6..a99b5d351 100644 --- a/src/api/transaction.ts +++ b/src/api/transaction.ts @@ -19,6 +19,8 @@ import { TransactionResponse, WaitForTransactionOptions, } from "../types"; +import { getSigningMessage } from "../internal/transactionSubmission"; +import { AnyRawTransaction } from "../transactions"; export class Transaction { readonly config: AptosConfig; @@ -140,4 +142,16 @@ export class Transaction { aptosConfig: this.config, }); } + + /** + * Returns a signing message for a transaction. + * + * This allows a user to sign a transaction using their own preferred signing method, and + * then submit it to the network. + * @param args.transaction A raw transaction for signing elsewhere + */ + // eslint-disable-next-line class-methods-use-this + getSigningMessage(args: { transaction: AnyRawTransaction }): Uint8Array { + return getSigningMessage(args); + } } diff --git a/src/internal/transactionSubmission.ts b/src/internal/transactionSubmission.ts index 121c42e2d..409244993 100644 --- a/src/internal/transactionSubmission.ts +++ b/src/internal/transactionSubmission.ts @@ -19,6 +19,7 @@ import { generateSignedTransactionForSimulation, generateSignedTransaction, sign, + generateSigningMessage, } from "../transactions/transactionBuilder/transactionBuilder"; import { InputGenerateTransactionData, @@ -154,6 +155,20 @@ function isMultiAgentTransactionInput( return "secondarySignerAddresses" in data; } +/** + * Builds a signing message that can be signed by external signers + * + * Note: Please prefer using `signTransaction` unless signing outside the SDK + * + * @param args.transaction AnyRawTransaction, as generated by `generateTransaction()` + * + * @return The message to be signed + */ +export function getSigningMessage(args: { transaction: AnyRawTransaction }): Uint8Array { + const { transaction } = args; + return generateSigningMessage(transaction); +} + /** * Sign a transaction that can later be submitted to chain * diff --git a/src/transactions/transactionBuilder/transactionBuilder.ts b/src/transactions/transactionBuilder/transactionBuilder.ts index eb3eceade..4573763aa 100644 --- a/src/transactions/transactionBuilder/transactionBuilder.ts +++ b/src/transactions/transactionBuilder/transactionBuilder.ts @@ -413,10 +413,8 @@ export function getAuthenticatorForSimulation(publicKey: PublicKey) { export function sign(args: { signer: Account; transaction: AnyRawTransaction }): AccountAuthenticator { const { signer, transaction } = args; - const transactionToSign = deriveTransactionType(transaction); - // get the signing message - const message = getSigningMessage(transactionToSign); + const message = generateSigningMessage(transaction); // account.signMessage const signerSignature = signer.sign(message); @@ -554,7 +552,8 @@ export function generateMultiSignersSignedTransaction( ); } -export function getSigningMessage(rawTxn: AnyRawTransactionInstance): Uint8Array { +export function generateSigningMessage(transaction: AnyRawTransaction): Uint8Array { + const rawTxn = deriveTransactionType(transaction); const hash = sha3Hash.create(); if (rawTxn instanceof RawTransaction) {