supra-l1-sdk-core
Version:
Core of the Supra-L1-SDK
369 lines (306 loc) • 14.1 kB
text/typescript
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0
/* eslint-disable max-len */
import nacl from "tweetnacl";
import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
import {
bcsSerializeBool,
bcsSerializeUint64,
bcsToBytes,
Bytes,
} from "../../bcs";
import { HexString } from "../../utils";
import {
TransactionBuilderEd25519,
TransactionBuilder,
} from "../../transaction_builder/index";
import {
ChainId,
Ed25519Signature,
RawTransaction,
Script,
EntryFunction,
StructTag,
TransactionArgumentAddress,
TransactionArgumentU8,
TransactionArgumentU8Vector,
TransactionPayloadScript,
TransactionPayloadEntryFunction,
TypeTagStruct,
TransactionArgumentU16,
TransactionArgumentU32,
TransactionArgumentU256,
AccountAddress,
TypeTagBool,
} from "../../supra_types";
const ADDRESS_1 = "0x1222";
const ADDRESS_2 = "0xdd";
const ADDRESS_3 = "0x0a550c18";
const ADDRESS_4 = "0x01";
const PRIVATE_KEY =
"9bf49a6a0755f953811fce125f2683d50429c3bb49e074147e0089a52eae155f";
const TXN_EXPIRE = "18446744073709551615";
function hexSignedTxn(signedTxn: Uint8Array): string {
return bytesToHex(signedTxn);
}
function sign(rawTxn: RawTransaction): Bytes {
const privateKeyBytes = new HexString(PRIVATE_KEY).toUint8Array();
const signingKey = nacl.sign.keyPair.fromSeed(privateKeyBytes.slice(0, 32));
const { publicKey } = signingKey;
const txnBuilder = new TransactionBuilderEd25519(
(signingMessage) =>
new Ed25519Signature(
nacl.sign(signingMessage, signingKey.secretKey).slice(0, 64),
),
publicKey,
);
return txnBuilder.sign(rawTxn);
}
test("throws when preparing signing message with invalid payload", () => {
expect(() => {
// @ts-ignore
TransactionBuilder.getSigningMessage("invalid");
}).toThrow("Unknown transaction type.");
});
test("gets the signing message", () => {
const entryFunctionPayload = new TransactionPayloadEntryFunction(
EntryFunction.natural(
`${ADDRESS_1}::aptos_coin`,
"transfer",
[],
[bcsToBytes(AccountAddress.fromHex(ADDRESS_2)), bcsSerializeUint64(1)],
),
);
const rawTxn = new RawTransaction(
AccountAddress.fromHex(new HexString(ADDRESS_3)),
BigInt(0),
entryFunctionPayload,
BigInt(2000),
BigInt(0),
BigInt(TXN_EXPIRE),
new ChainId(4),
);
const message = TransactionBuilder.getSigningMessage(rawTxn);
expect(message instanceof Uint8Array).toBeTruthy();
expect(HexString.fromUint8Array(message).hex()).toBe(
"0x88d16ed1aba6d77d521aafada6d6e92d7a174527adb457adfa66a9c5a0f1fe37000000000000000000000000000000000000000000000000000000000a550c1800000000000000000200000000000000000000000000000000000000000000000000000000000012220a6170746f735f636f696e087472616e7366657200022000000000000000000000000000000000000000000000000000000000000000dd080100000000000000d0070000000000000000000000000000ffffffffffffffff04",
);
});
test("serialize entry function payload with no type args", () => {
const entryFunctionPayload = new TransactionPayloadEntryFunction(
EntryFunction.natural(
`${ADDRESS_1}::aptos_coin`,
"transfer",
[],
[bcsToBytes(AccountAddress.fromHex(ADDRESS_2)), bcsSerializeUint64(1)],
),
);
const rawTxn = new RawTransaction(
AccountAddress.fromHex(new HexString(ADDRESS_3)),
BigInt(0),
entryFunctionPayload,
BigInt(2000),
BigInt(0),
BigInt(TXN_EXPIRE),
new ChainId(4),
);
const signedTxn = sign(rawTxn);
expect(hexSignedTxn(signedTxn)).toBe(
"000000000000000000000000000000000000000000000000000000000a550c1800000000000000000200000000000000000000000000000000000000000000000000000000000012220a6170746f735f636f696e087472616e7366657200022000000000000000000000000000000000000000000000000000000000000000dd080100000000000000d0070000000000000000000000000000ffffffffffffffff040020b9c6ee1630ef3e711144a648db06bbb2284f7274cfbee53ffcee503cc1a4920040d691513e702524d5ffc28e27d68e946b5ca29f33c9b7566c03a7e874264edb9510bd839f87e4c5ed938c3aa1f121b61fab16d1a4e0fb96b9b0656732159bc20b",
);
});
test("serialize entry function payload with type args", () => {
const token = new TypeTagStruct(
StructTag.fromString(`${ADDRESS_4}::aptos_coin::AptosCoin`),
);
const entryFunctionPayload = new TransactionPayloadEntryFunction(
EntryFunction.natural(
`${ADDRESS_1}::coin`,
"transfer",
[token],
[bcsToBytes(AccountAddress.fromHex(ADDRESS_2)), bcsSerializeUint64(1)],
),
);
const rawTxn = new RawTransaction(
AccountAddress.fromHex(ADDRESS_3),
BigInt(0),
entryFunctionPayload,
BigInt(2000),
BigInt(0),
BigInt(TXN_EXPIRE),
new ChainId(4),
);
const signedTxn = sign(rawTxn);
expect(hexSignedTxn(signedTxn)).toBe(
"000000000000000000000000000000000000000000000000000000000a550c18000000000000000002000000000000000000000000000000000000000000000000000000000000122204636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00022000000000000000000000000000000000000000000000000000000000000000dd080100000000000000d0070000000000000000000000000000ffffffffffffffff040020b9c6ee1630ef3e711144a648db06bbb2284f7274cfbee53ffcee503cc1a4920040d66b69e7cc9ff83f9c41509c0843e89a535586b56473ef795a6ebdda617a2103d661849ca42f5165dfda7bddc74a3e189984d85c2ce761d0df107edd793a0706",
);
});
test("serialize entry function payload with type args but no function args", () => {
const token = new TypeTagStruct(
StructTag.fromString(`${ADDRESS_4}::aptos_coin::AptosCoin`),
);
const entryFunctionPayload = new TransactionPayloadEntryFunction(
EntryFunction.natural(`${ADDRESS_1}::coin`, "fake_func", [token], []),
);
const rawTxn = new RawTransaction(
AccountAddress.fromHex(ADDRESS_3),
BigInt(0),
entryFunctionPayload,
BigInt(2000),
BigInt(0),
BigInt(TXN_EXPIRE),
new ChainId(4),
);
const signedTxn = sign(rawTxn);
expect(hexSignedTxn(signedTxn)).toBe(
"000000000000000000000000000000000000000000000000000000000a550c18000000000000000002000000000000000000000000000000000000000000000000000000000000122204636f696e0966616b655f66756e63010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e0000d0070000000000000000000000000000ffffffffffffffff040020b9c6ee1630ef3e711144a648db06bbb2284f7274cfbee53ffcee503cc1a4920040f6afe4732eafd0d4ae677d50512a2803877b63a381eaaa1290e6873818b32a633c9805522e78085d17210c8960db7739559a97403af1bffaa8c29ff0b6f3d207",
);
});
test("serialize entry function payload with generic type args and function args", () => {
const token = new TypeTagStruct(StructTag.fromString(`0x14::token::Token`));
const entryFunctionPayload = new TransactionPayloadEntryFunction(
EntryFunction.natural(
`${ADDRESS_1}::aptos_token`,
"fake_typed_func",
[token, new TypeTagBool()],
[bcsToBytes(AccountAddress.fromHex(ADDRESS_2)), bcsSerializeBool(true)],
),
);
const rawTxn = new RawTransaction(
AccountAddress.fromHex(ADDRESS_3),
BigInt(0),
entryFunctionPayload,
BigInt(2000),
BigInt(0),
BigInt(TXN_EXPIRE),
new ChainId(4),
);
const signedTxn = sign(rawTxn);
expect(hexSignedTxn(signedTxn)).toBe(
"000000000000000000000000000000000000000000000000000000000a550c1800000000000000000200000000000000000000000000000000000000000000000000000000000012220b6170746f735f746f6b656e0f66616b655f74797065645f66756e630207000000000000000000000000000000000000000000000000000000000000001405746f6b656e05546f6b656e0000022000000000000000000000000000000000000000000000000000000000000000dd0101d0070000000000000000000000000000ffffffffffffffff040020b9c6ee1630ef3e711144a648db06bbb2284f7274cfbee53ffcee503cc1a4920040a51b95ad0bad361fa6f860084682a873bf212bf1d76158f9417b5cd5b27125f55cc0de4943c738083f02230128112f098e34e8cd73cf61fb4a2947b2b4f2fb02",
);
});
test("serialize script payload with no type args and no function args", () => {
const script = hexToBytes(
"a11ceb0b030000000105000100000000050601000000000000000600000000000000001a0102",
);
const scriptPayload = new TransactionPayloadScript(
new Script(script, [], []),
);
const rawTxn = new RawTransaction(
AccountAddress.fromHex(ADDRESS_3),
BigInt(0),
scriptPayload,
BigInt(2000),
BigInt(0),
BigInt(TXN_EXPIRE),
new ChainId(4),
);
const signedTxn = sign(rawTxn);
expect(hexSignedTxn(signedTxn)).toBe(
"000000000000000000000000000000000000000000000000000000000a550c1800000000000000000026a11ceb0b030000000105000100000000050601000000000000000600000000000000001a01020000d0070000000000000000000000000000ffffffffffffffff040020b9c6ee1630ef3e711144a648db06bbb2284f7274cfbee53ffcee503cc1a49200406a632927121af23218a065f0bf964897d05353893a079c8ab9ac9c4089f6d1938db71bc025b56d3324e77b444c44c06eaddefac92a6b6ec1a36fca30359ea702",
);
});
test("serialize script payload with type args but no function args", () => {
const token = new TypeTagStruct(
StructTag.fromString(`${ADDRESS_4}::aptos_coin::AptosCoin`),
);
const script = hexToBytes(
"a11ceb0b030000000105000100000000050601000000000000000600000000000000001a0102",
);
const scriptPayload = new TransactionPayloadScript(
new Script(script, [token], []),
);
const rawTxn = new RawTransaction(
AccountAddress.fromHex(ADDRESS_3),
BigInt(0),
scriptPayload,
BigInt(2000),
BigInt(0),
BigInt(TXN_EXPIRE),
new ChainId(4),
);
const signedTxn = sign(rawTxn);
expect(hexSignedTxn(signedTxn)).toBe(
"000000000000000000000000000000000000000000000000000000000a550c1800000000000000000026a11ceb0b030000000105000100000000050601000000000000000600000000000000001a0102010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e0000d0070000000000000000000000000000ffffffffffffffff040020b9c6ee1630ef3e711144a648db06bbb2284f7274cfbee53ffcee503cc1a49200407cf315e4cb53809376ca56c1772cd61222541630eabd177ab3ae2a80de4645d30f891be3f6a11174b7c5b1627a4da22deefd160fa476800f7c763a1ef276a30f",
);
});
test("serialize script payload with type arg and function arg", () => {
const token = new TypeTagStruct(
StructTag.fromString(`${ADDRESS_4}::aptos_coin::AptosCoin`),
);
const argU8 = new TransactionArgumentU8(2);
const script = hexToBytes(
"a11ceb0b030000000105000100000000050601000000000000000600000000000000001a0102",
);
const scriptPayload = new TransactionPayloadScript(
new Script(script, [token], [argU8]),
);
const rawTxn = new RawTransaction(
AccountAddress.fromHex(ADDRESS_3),
BigInt(0),
scriptPayload,
BigInt(2000),
BigInt(0),
BigInt(TXN_EXPIRE),
new ChainId(4),
);
const signedTxn = sign(rawTxn);
expect(hexSignedTxn(signedTxn)).toBe(
"000000000000000000000000000000000000000000000000000000000a550c1800000000000000000026a11ceb0b030000000105000100000000050601000000000000000600000000000000001a0102010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00010002d0070000000000000000000000000000ffffffffffffffff040020b9c6ee1630ef3e711144a648db06bbb2284f7274cfbee53ffcee503cc1a492004065ad5e3d0bb40d8332a001ad836112860077d5e0854026a03d88c740ca8b0011f15eaea2f5c6ff89ee8432d33c10ff05c9f20d4619b9db38dacf3fb0b7ca1a09",
);
});
test("serialize script payload with one type arg and two function args", () => {
const token = new TypeTagStruct(
StructTag.fromString(`${ADDRESS_4}::aptos_coin::AptosCoin`),
);
const argU8Vec = new TransactionArgumentU8Vector(bcsSerializeUint64(1));
const argAddress = new TransactionArgumentAddress(
AccountAddress.fromHex("0x01"),
);
const script = hexToBytes(
"a11ceb0b030000000105000100000000050601000000000000000600000000000000001a0102",
);
const scriptPayload = new TransactionPayloadScript(
new Script(script, [token], [argU8Vec, argAddress]),
);
const rawTxn = new RawTransaction(
AccountAddress.fromHex(ADDRESS_3),
BigInt(0),
scriptPayload,
BigInt(2000),
BigInt(0),
BigInt(TXN_EXPIRE),
new ChainId(4),
);
const signedTxn = sign(rawTxn);
expect(hexSignedTxn(signedTxn)).toBe(
"000000000000000000000000000000000000000000000000000000000a550c1800000000000000000026a11ceb0b030000000105000100000000050601000000000000000600000000000000001a0102010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000204080100000000000000030000000000000000000000000000000000000000000000000000000000000001d0070000000000000000000000000000ffffffffffffffff040020b9c6ee1630ef3e711144a648db06bbb2284f7274cfbee53ffcee503cc1a49200402fd66c067ed4e97cf02ac9c731d920c98f6ef07860bc6a9773ba5fdeb7c78880fe8cbcab9e0ff0d1c45ae085e98594cd735a837f3e8deaac69fc743a4433160f",
);
});
test("serialize script payload with new integer types (u16, u32, u256) as args", () => {
const argU16 = new TransactionArgumentU16(0xf111);
const argU32 = new TransactionArgumentU32(0xf1111111);
const argU256 = new TransactionArgumentU256(
BigInt(
"0xf111111111111111111111111111111111111111111111111111111111111111",
),
);
const script = hexToBytes("");
const scriptPayload = new TransactionPayloadScript(
new Script(script, [], [argU16, argU32, argU256]),
);
const rawTxn = new RawTransaction(
AccountAddress.fromHex(ADDRESS_3),
BigInt(0),
scriptPayload,
BigInt(2000),
BigInt(0),
BigInt(TXN_EXPIRE),
new ChainId(4),
);
const signedTxn = sign(rawTxn);
expect(hexSignedTxn(signedTxn)).toBe(
"000000000000000000000000000000000000000000000000000000000a550c180000000000000000000000030611f107111111f10811111111111111111111111111111111111111111111111111111111111111f1d0070000000000000000000000000000ffffffffffffffff040020b9c6ee1630ef3e711144a648db06bbb2284f7274cfbee53ffcee503cc1a49200400f9397e03c6bfc7cd3c98c2fdbf46171feaea56e5188e0cd6d87d2496291f68181fd7ecda1363aa010440cf485f3086ec5b8f81c4a6aa74ef6c1b5d0fea0670b",
);
});