diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml
index 01b4eb9..b901dbd 100644
--- a/.github/workflows/node.js.yml
+++ b/.github/workflows/node.js.yml
@@ -15,7 +15,7 @@ jobs:
strategy:
matrix:
- node-version: [16.x]
+ node-version: [20.x]
steps:
- uses: actions/checkout@v2
diff --git a/.tool-versions b/.tool-versions
index 5686ee0..ffb152e 100644
--- a/.tool-versions
+++ b/.tool-versions
@@ -1 +1 @@
-nodejs 18.12.1
+nodejs 20.17.0
diff --git a/README.md b/README.md
index 010a8f7..d071aa1 100644
--- a/README.md
+++ b/README.md
@@ -375,11 +375,27 @@ Define the transaction type
- `type` is the string defining the type of transaction to generate ("keychain", "keychain_access", "transfer", "hosting", "code_proposal", "code_approval", "token")
-#### setCode(code)
+#### setContract(contract)
-Add the code in the `data.code` section of the transaction
+Add the contract in the `data.contract` section of the transaction
-- `code` is a string defining the smart contract
+- `contract` is an object with following keys:
+ - `bytecode` Uint8Array of the compiled wasm code compressed using zip
+ - `manifest` the manifest of the contrat containing actions and functions spec
+
+```js
+const bytecode = fs.readFileSync("./dist/contract.wasm")
+const manifestFile = fs.readFileSync('./dist/manifest.json', 'utf-8')
+const manifest = JSON.parse(manifestFile)
+
+const contract = new Contract(bytecode, manifest)
+
+const txBuilder = archethic.transaction.new().setType("contract").setContract(contract)
+// or use Contract function
+import { Contract } from "@archethicjs/sdk"
+const archethic = new Archethic("https://testnet.archethic.net")
+const tx = Contract.newContractTransaction(archethic, contract, seed)
+```
#### setGenerateEncryptedSeedSC(flag)
@@ -423,8 +439,8 @@ Add a token transfer to the `data.ledger.token.transfers` section of the transac
Adds a recipient to call the smart contract's "transaction" action.
- `to` is the contract's address in hexadecimal or Uint8Array
-- `action` is the name of the action. This parameter is not mandatory
-- `args` is the list of arguments for the action (must contain only JSON valid data). This parameter is not mandatory
+- `action` is the name of the action
+- `args` is an object containing the parameter for the contract action. This parameter is not mandatory
```js
import Archethic from "@archethicjs/sdk";
@@ -433,8 +449,8 @@ const archethic = new Archethic("https://testnet.archethic.net");
const tx = archethic.transaction
.new()
.setType("transfer")
- .addRecipient("0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646")
- .addRecipient("0000bc96b1a9751d3750edb9381a55b5b4e4fb104c10b0b6c9a00433ec464637bfab", "vote", ["Dr. Who"]);
+ .addRecipient("0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", "increment")
+ .addRecipient("0000bc96b1a9751d3750edb9381a55b5b4e4fb104c10b0b6c9a00433ec464637bfab", "vote", {name: "Dr. Who"});
```
#### build(seed, index, curve, hashAlgo)
@@ -829,6 +845,34 @@ const storageNoncePublicKey = await archethic.network.getStorageNoncePublicKey()
// 00b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646
```
+### getContractCode(address)
+
+Return the contract code
+
+- `address`: string or Uint8Array of the contract address
+
+```js
+import Archethic from "@archethicjs/sdk"
+
+const archethic = new Archethic("https://testnet.archethic.net")
+await archethic.connect()
+
+const res = archethic.network.getContractCode("0001234...")
+console.log(res)
+{
+ code: "...",
+ contract: {
+ bytecode: "00231654",
+ manifest: {
+ abi: {
+ state: ...,
+ functions: []
+ }
+ }
+ }
+}
+```
+
### getOracleData(timestamp)
Fetch the OracleChain data
@@ -1375,15 +1419,31 @@ tx.originSign(originPrivateKey)
- ### newContractTransaction
+ ### newContractTransaction(archethic, contract, contractSeed, txData?)
- Create a new contract transaction injecting the code and the authorized public key encryption to allow node to emit transaction on your behalf
+ Create a new contract transaction injecting the bytecode, manifest and the authorized public key encryption to allow node to emit transaction on your behalf. Returned transaction is already signed.
```js
import Archethic, { Utils, Contract } from "@archethicjs/sdk"
const archethic = new Archethic("https://testnet.archethic.net")
- const tx = await Contract.newContractTransaction(archethic, contractCode, contractSeed)
+ const bytecode = fs.readFileSync("./dist/contract.wasm")
+ const manifestFile = fs.readFileSync('./dist/manifest.json', 'utf-8')
+ const manifest = JSON.parse(manifestFile)
+
+ const contract = new Contract(bytecode, manifest)
+
+ // txData is optional
+ const txData = {
+ content: "content",
+ ledger: {
+ uco: {
+ transfers: [{to: "1234", amount: Utils.parseBigInt("10")}]
+ }
+ }
+ }
+
+ const tx = await Contract.newContractTransaction(archethic, contract, contractSeed, txData)
tx
.originSign(Utils.originPrivateKey)
@@ -1392,7 +1452,29 @@ tx.originSign(originPrivateKey)
.send();
```
+ ### updateContractTransaction(archethic, contractAddress, contract)
+
+ Create a new transaction containing the recipient filled with appropriated function to update a contract code
+
+ ```js
+ import Archethic, { Utils, Contract } from "@archethicjs/sdk"
+ const archethic = new Archethic("https://testnet.archethic.net")
+
+ const bytecode = fs.readFileSync("./dist/contract.wasm")
+ const manifestFile = fs.readFileSync('./dist/manifest.json', 'utf-8')
+ const manifest = JSON.parse(manifestFile)
+
+ const contract = new Contract(bytecode, manifest)
+
+ const tx = await Contract.updateContractTransaction(archethic, contractAddress, contract)
+ tx
+ .build(seed, index)
+ .originSign(Utils.originPrivateKey)
+ .on("requiredConfirmation", () => console.log("ok updated"))
+ .on("error", (context, reason) => console.error(reason))
+ .send();
+ ```
diff --git a/example/transactionBuilder/app.js b/example/transactionBuilder/app.js
index 154f608..96b1196 100644
--- a/example/transactionBuilder/app.js
+++ b/example/transactionBuilder/app.js
@@ -1,9 +1,12 @@
import Archethic, { Utils, Crypto, Contract } from "@archethicjs/sdk";
import { ExtendedTransactionBuilder } from "../../dist/transaction";
+import { Contract as ContractCode } from "../../dist/contract";
const { parseBigInt, formatBigInt } = Utils;
-let file_content = "";
+let file_content = ""
+let bytecode = new Uint8Array();
+let manifest = {};
let ucoTransfers = [];
let tokenTransfers = [];
@@ -80,8 +83,7 @@ window.generate_transaction = async () => {
const seed = document.querySelector("#seed").value;
- const code = document.querySelector("#code").value;
- if (code != "") {
+ if (bytecode.byteLength > 0) {
const ownershipIndex = ownerships.findIndex(function (ownership) {
return ownership.secret == seed;
});
@@ -113,8 +115,11 @@ window.generate_transaction = async () => {
txBuilder = archethic.transaction
.new()
.setType(document.querySelector("#type").value)
- .setCode(document.querySelector("#code").value)
- .setContent(content);
+ .setContent(content)
+
+ if (bytecode.byteLength > 0) {
+ txBuilder.setContract(new ContractCode(bytecode, manifest))
+ }
ownerships.forEach(function (ownership) {
const secretKey = Crypto.randomSecretKey();
@@ -245,18 +250,17 @@ window.onClickAddTokenTransfer = async () => {
document.querySelector("#token_id").value = "0";
};
-let namedParams = [];
+let namedParams = []
+let objectParams = {};
window.onChangeRecipient = async () => {
const address = document.querySelector("#recipient").value;
- const contractCode = await archethic.network.getContractCode(address);
- if (contractCode == "") {
- return;
- }
+ const contractContext = await archethic.network.getContractCode(address);
document.querySelector("#namedActionsContainer").style.display = "block";
- Contract.extractActionsFromContract(contractCode).forEach((action) => {
+ const actions = contractContext.contract ? contractContext.contract.getActions() : Contract.extractActionsFromContract(contractContext.code);
+ actions.forEach((action) => {
const option = document.createElement("option");
option.text = action.name;
option.value = action.name;
@@ -269,6 +273,8 @@ window.onChangeRecipient = async () => {
paramsContainer.setAttribute("style", "display: none");
paramsContainer.setAttribute("class", "namedActionParams");
+ namedParams = new Array(action.parameters.length).fill(null)
+
action.parameters.forEach((parameter, index) => {
const inputId = paramsContainerId + "_param_" + parameter;
const paramLabel = document.createElement("label");
@@ -280,7 +286,30 @@ window.onChangeRecipient = async () => {
paramInput.setAttribute("class", "input");
paramInput.addEventListener("change", function (e) {
const value = e.target.value;
- namedParams[index] = Contract.parseTypedArgument(value);
+ try {
+ const json = JSON.parse(value);
+ if (typeof json === "object") {
+ if (contractContext.contract) {
+ objectParams[parameter] = Contract.parseTypedArgument(json);
+ } else {
+ namedParams[index] = Contract.parseTypedArgument(json);
+ }
+ } else {
+ if (contractContext.contract) {
+ objectParams[parameter] = Contract.parseTypedArgument(value);
+ }
+ else {
+ namedParams[index] = Contract.parseTypedArgument(value);
+ }
+ }
+ } catch (e) {
+ if (contractContext.contract) {
+ objectParams[parameter] = Contract.parseTypedArgument(value);
+ }
+ else {
+ namedParams[index] = Contract.parseTypedArgument(value);
+ }
+ }
});
paramsContainer.appendChild(paramLabel);
@@ -305,11 +334,11 @@ window.onClickAddRecipient = () => {
const recipientList = document.querySelector("#recipients");
if (namedAction != "") {
- recipients.push({ address: recipientAddress, action: namedAction, args: namedParams });
+ recipients.push({ address: recipientAddress, action: namedAction, args: Object.keys(objectParams).length > 0 ? objectParams : namedParams });
if (recipientList.textContent != "") {
recipientList.textContent = recipientList.textContent + "\n";
}
- recipientList.textContent += `${recipientAddress} - ${namedAction} - ${namedParams}`;
+ recipientList.textContent += `${recipientAddress} - ${namedAction} - ${JSON.stringify(Object.keys(objectParams).length > 0 ? objectParams : namedParams)}`;
document.querySelector("#namedActionsContainer").style.display = "none";
document.querySelector("#namedActions").innerHTML = "";
@@ -355,7 +384,7 @@ window.sendTransaction = async () => {
if (error.data.data) {
errMsg += `
(${error.data.data.code}) ${error.data.data.message}
`;
} else {
- errMsg += `${error.data.message}
`;
+ errMsg += `${JSON.stringify(error.data.message)}
`;
}
}
@@ -390,6 +419,26 @@ document.querySelector("#content_upload").addEventListener("change", (event) =>
fr.readAsArrayBuffer(fileList[0]);
});
+document.querySelector("#bytecode_upload").addEventListener("change", (event) => {
+ const fileList = event.target.files;
+
+ const fr = new FileReader();
+ fr.onload = function (e) {
+ bytecode = new Uint8Array(e.target.result);
+ };
+ fr.readAsArrayBuffer(fileList[0]);
+});
+
+document.querySelector("#manifest_upload").addEventListener("change", (event) => {
+ const fileList = event.target.files;
+
+ const fr = new FileReader();
+ fr.onload = function (e) {
+ manifest = JSON.parse(new TextDecoder().decode(e.target.result))
+ };
+ fr.readAsArrayBuffer(fileList[0]);
+});
+
window.addOwnership = () => {
const ownershipIndex = ownerships.length;
diff --git a/example/transactionBuilder/index.html b/example/transactionBuilder/index.html
index a685522..0dafcd1 100644
--- a/example/transactionBuilder/index.html
+++ b/example/transactionBuilder/index.html
@@ -60,14 +60,21 @@ Transaction Builder
-
-
+
-
-
-
+
diff --git a/package-lock.json b/package-lock.json
index 8c1da38..3f32e8b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -21,6 +21,7 @@
"isomorphic-ws": "^5.0.0",
"js-sha3": "^0.9.0",
"json-rpc-2.0": "^1.6.0",
+ "pako": "^2.1.0",
"phoenix": "^1.7.2",
"sjcl": "^1.0.8",
"tweetnacl": "^1.0.3",
@@ -29,9 +30,11 @@
"devDependencies": {
"@types/absinthe__socket": "^0.2.3",
"@types/chrome": "0.0.266",
+ "@types/crypto-js": "^4.2.2",
"@types/ed2curve": "^0.2.4",
"@types/elliptic": "^6.4.14",
"@types/jest": "^29.5.0",
+ "@types/pako": "^2.0.3",
"@types/sjcl": "^1.0.34",
"esbuild": "^0.19.0",
"jest": "^29.5.0",
@@ -1379,6 +1382,16 @@
"@types/har-format": "*"
}
},
+<<<<<<< HEAD
+=======
+ "node_modules/@types/crypto-js": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz",
+ "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+>>>>>>> fbd6a7b (improve types with CryptoJS)
"node_modules/@types/ed2curve": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/@types/ed2curve/-/ed2curve-0.2.4.tgz",
@@ -1470,6 +1483,13 @@
"undici-types": "~6.19.8"
}
},
+ "node_modules/@types/pako": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.3.tgz",
+ "integrity": "sha512-bq0hMV9opAcrmE0Byyo0fY3Ew4tgOevJmQ9grUhpXQhYfyLJ1Kqg3P33JT5fdbT2AjeAjR51zqqVjAL/HMkx7Q==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/phoenix": {
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.5.tgz",
@@ -3738,6 +3758,12 @@
"node": ">=6"
}
},
+ "node_modules/pako": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
+ "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
+ "license": "(MIT AND Zlib)"
+ },
"node_modules/parse-json": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@@ -5451,6 +5477,15 @@
"@types/har-format": "*"
}
},
+<<<<<<< HEAD
+=======
+ "@types/crypto-js": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz",
+ "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==",
+ "dev": true
+ },
+>>>>>>> fbd6a7b (improve types with CryptoJS)
"@types/ed2curve": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/@types/ed2curve/-/ed2curve-0.2.4.tgz",
@@ -5542,6 +5577,12 @@
"undici-types": "~6.19.8"
}
},
+ "@types/pako": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.3.tgz",
+ "integrity": "sha512-bq0hMV9opAcrmE0Byyo0fY3Ew4tgOevJmQ9grUhpXQhYfyLJ1Kqg3P33JT5fdbT2AjeAjR51zqqVjAL/HMkx7Q==",
+ "dev": true
+ },
"@types/phoenix": {
"version": "1.6.5",
"resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.5.tgz",
@@ -7234,6 +7275,11 @@
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
},
+ "pako": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
+ "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="
+ },
"parse-json": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
diff --git a/package.json b/package.json
index 2a59d24..cc2c699 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@archethicjs/sdk",
- "version": "1.21.3",
+ "version": "2.0.0-rc.0",
"description": "Archethic Javascript SDK",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -32,9 +32,11 @@
"devDependencies": {
"@types/absinthe__socket": "^0.2.3",
"@types/chrome": "0.0.266",
+ "@types/crypto-js": "^4.2.2",
"@types/ed2curve": "^0.2.4",
"@types/elliptic": "^6.4.14",
"@types/jest": "^29.5.0",
+ "@types/pako": "^2.0.3",
"@types/sjcl": "^1.0.34",
"esbuild": "^0.19.0",
"jest": "^29.5.0",
@@ -56,6 +58,7 @@
"isomorphic-ws": "^5.0.0",
"js-sha3": "^0.9.0",
"json-rpc-2.0": "^1.6.0",
+ "pako": "^2.1.0",
"phoenix": "^1.7.2",
"sjcl": "^1.0.8",
"tweetnacl": "^1.0.3",
diff --git a/src/api.ts b/src/api.ts
index 6fa1648..0153fcd 100644
--- a/src/api.ts
+++ b/src/api.ts
@@ -1,8 +1,9 @@
import fetch from "cross-fetch";
import absinthe from "./api/absinthe.js";
-import { maybeUint8ArrayToHex } from "./utils.js";
+import { hexToUint8Array, maybeUint8ArrayToHex } from "./utils.js";
import { Balance, NearestEndpoint, OracleData, Ownership, Token } from "./types.js";
import Transaction from "./transaction.js";
+import { Contract } from "./contract.js";
/**
* Send a custom query to the Archethic API
@@ -332,7 +333,12 @@ export async function getBalance(address: string | Uint8Array, endpoint: string
});
}
-export async function getContractCode(address: string | Uint8Array, endpoint: string | URL): Promise
{
+export type ContractCode = {
+ code: string;
+ contract?: Contract;
+};
+
+export async function getContractCode(address: string | Uint8Array, endpoint: string | URL): Promise {
address = maybeUint8ArrayToHex(address);
const url = new URL("/api", endpoint);
@@ -346,18 +352,36 @@ export async function getContractCode(address: string | Uint8Array, endpoint: st
query: `query {
lastTransaction(address: "${address}") {
data {
- code
+ code,
+ contract {
+ bytecode,
+ manifest {
+ abi {
+ functions,
+ state
+ }
+ }
+ }
}
}
}`
})
})
.then(handleResponse)
- .then((res): string => {
+ .then((res): ContractCode => {
if (res.errors || res.data == null) {
- return "";
+ throw new Error("No contract at this address");
} else {
- return res.data.lastTransaction.data.code;
+ return {
+ code: res.data.lastTransaction.data.code,
+ contract: res.data.lastTransaction.data.contract
+ ? new Contract(
+ hexToUint8Array(res.data.lastTransaction.data.contract.bytecode),
+ res.data.lastTransaction.data.contract.manifest,
+ false
+ )
+ : undefined
+ };
}
});
}
diff --git a/src/api/node_rpc.ts b/src/api/node_rpc.ts
index 450d83f..c7db9ca 100644
--- a/src/api/node_rpc.ts
+++ b/src/api/node_rpc.ts
@@ -39,7 +39,7 @@ export class NodeRPCClient {
* @returns {Promise} The transaction fee
*/
async getTransactionFee(tx: TransactionBuilder): Promise {
- return this.client.request("estimate_transaction_fee", { transaction: tx.toNodeRPC() });
+ return this.client.request("estimate_transaction_fee", { transaction: await tx.toNodeRPC() });
}
/**
@@ -48,7 +48,7 @@ export class NodeRPCClient {
* @returns {Promise} The transaction response
*/
async sendTransaction(tx: TransactionBuilder): Promise {
- return this.client.request("send_transaction", { transaction: tx.toNodeRPC() });
+ return this.client.request("send_transaction", { transaction: await tx.toNodeRPC() });
}
/**
@@ -66,7 +66,7 @@ export class NodeRPCClient {
* @returns {Promise} The simulation response per recipient
*/
async simulateContractExecution(tx: TransactionBuilder): Promise {
- return this.client.request("simulate_contract_execution", { transaction: tx.toNodeRPC() });
+ return this.client.request("simulate_contract_execution", { transaction: await tx.toNodeRPC() });
}
async handleRequest(jsonRPCRequest: any): Promise {
diff --git a/src/contract.ts b/src/contract.ts
index 984663d..ec84e2c 100644
--- a/src/contract.ts
+++ b/src/contract.ts
@@ -1,12 +1,30 @@
-import { ContractAction } from "./types.js";
+import { ContractAction, TransactionData } from "./types.js";
import { encryptSecret, deriveAddress } from "./crypto.js";
import { ExtendedTransactionBuilder } from "./transaction.js";
import Archethic from "./index.js";
+import { deflateRaw } from "pako";
+
+export type ContractManifest = {
+ abi: WasmABI;
+};
+
+export type WasmABI = {
+ state: Record;
+ functions: Record;
+};
+
+export type WASMFunctionABI = {
+ type: string;
+ triggerType?: string;
+ name: string;
+ input: Record;
+};
export function extractActionsFromContract(code: string): ContractAction[] {
+ let actions = [];
+
const regex = /actions\s+triggered_by:\s+transaction,\s+on:\s+([\w\s.,()]+?)\s+do/g;
- let actions = [];
for (const match of code.matchAll(regex)) {
const fullAction = match[1];
@@ -43,17 +61,63 @@ This function abstract the wrapping of encrypted keys towards the node's shared
*/
export async function newContractTransaction(
archethic: Archethic,
- code: string,
- seed: string | Uint8Array
+ contract: Contract,
+ seed: string | Uint8Array,
+ txData?: TransactionData
): Promise {
const storageNoncePublicKey = await archethic.network.getStorageNoncePublicKey();
const index = await archethic.transaction.getTransactionIndex(deriveAddress(seed, 0));
const { encryptedSecret, authorizedKeys } = encryptSecret(seed, storageNoncePublicKey);
- return archethic.transaction
+
+ const tx = archethic.transaction
.new()
.setType("contract")
- .setCode(code)
- .addOwnership(encryptedSecret, authorizedKeys)
- .build(seed, index);
+ .setContract(contract)
+ .addOwnership(encryptedSecret, authorizedKeys);
+
+ if (txData) {
+ txData.ledger.uco.transfers.forEach((t) => tx.addUCOTransfer(t.to, t.amount));
+ txData.ledger.token.transfers.forEach((t) => tx.addTokenTransfer(t.to, t.amount, t.tokenAddress, t.tokenId));
+ txData.recipients.forEach((r) => tx.addRecipient(r.address, r.action, r.args));
+ tx.setContent(txData.content);
+ }
+
+ return tx.build(seed, index);
+}
+
+export function updateContractTransaction(
+ archethic: Archethic,
+ contractAddress: string,
+ contract: Contract
+): ExtendedTransactionBuilder {
+ return archethic.transaction
+ .new()
+ .setType("transfer")
+ .addRecipient(contractAddress, "upgrade", contract);
+}
+
+export class Contract {
+ bytecode: Uint8Array;
+ manifest: ContractManifest;
+
+ constructor(bytecode: Uint8Array, manifest: ContractManifest, compress: boolean = true) {
+ this.bytecode = compress ? deflateRaw(bytecode) : bytecode;
+ this.manifest = manifest;
+ }
+
+ getActions(): ContractAction[] {
+ let actions: ContractAction[] = [];
+ for (let name of Object.keys(this.manifest.abi.functions)) {
+ const functionABI = this.manifest.abi.functions[name];
+ console.log(functionABI)
+ if (functionABI.type == "action" && functionABI.triggerType == "transaction") {
+ actions.push({
+ name: name,
+ parameters: functionABI.input ? Object.keys(functionABI.input) : []
+ });
+ }
+ }
+ return actions;
+ }
}
diff --git a/src/network.ts b/src/network.ts
index fb0878d..8adb6de 100644
--- a/src/network.ts
+++ b/src/network.ts
@@ -44,7 +44,7 @@ export default class Network {
return this.core.requestNode((endpoint) => API.rawGraphQLQuery(query, endpoint));
}
- async getContractCode(address: string): Promise {
+ async getContractCode(address: string): Promise {
return this.core.requestNode((endpoint) => API.getContractCode(address, endpoint));
}
}
diff --git a/src/transaction_builder.ts b/src/transaction_builder.ts
index 846f9f3..636a2a9 100644
--- a/src/transaction_builder.ts
+++ b/src/transaction_builder.ts
@@ -5,7 +5,7 @@ import {
HashAlgorithm,
TransactionData,
UserTypeTransaction,
- TransactionRPC
+ TransactionRPC,
} from "./types.js";
import {
concatUint8Arrays,
@@ -18,8 +18,9 @@ import {
} from "./utils.js";
import TE from "./typed_encoding.js";
import { deriveAddress, deriveKeyPair, sign } from "./crypto.js";
+import { Contract } from "./contract.js";
-const VERSION = 3;
+export const VERSION = 4;
function getTransactionTypeId(type: UserTypeTransaction): number {
switch (type) {
@@ -62,9 +63,8 @@ export default class TransactionBuilder {
this.type = type as UserTypeTransaction;
this.address = new Uint8Array();
this.data = {
- content: new Uint8Array(),
- code: new Uint8Array(),
ownerships: [],
+ content: "",
ledger: {
uco: {
transfers: []
@@ -89,9 +89,9 @@ export default class TransactionBuilder {
if (!Object.keys(UserTypeTransaction).includes(type)) {
throw new Error(
"Transaction type must be one of " +
- Object.keys(UserTypeTransaction)
- .map((t) => `'${t}'`)
- .join(", ")
+ Object.keys(UserTypeTransaction)
+ .map((t) => `'${t}'`)
+ .join(", ")
);
}
this.type = type as UserTypeTransaction;
@@ -99,22 +99,19 @@ export default class TransactionBuilder {
}
/**
- * Add smart contract code to the transcation
- * @param {string} code Smart contract code
+ * Add smart contract's definition to the transcation
+ * @param {Contract} code Smart contract code
*/
- setCode(code: string) {
- this.data.code = new TextEncoder().encode(code);
+ setContract(contract: Contract) {
+ this.data.contract = contract;
return this;
}
/**
* Add a content to the transaction
- * @param {String | Uint8Array} content Hosted content
+ * @param {String} content Hosted content
*/
- setContent(content: string | Uint8Array) {
- if (typeof content == "string") {
- content = new TextEncoder().encode(content);
- }
+ setContent(content: string) {
this.data.content = content;
return this;
}
@@ -202,21 +199,21 @@ export default class TransactionBuilder {
* Add recipient to the transaction
* @param {string | Uint8Array} to Recipient address (hexadecimal or binary buffer)
* @param {string} action The named action
- * @param {any[]} args The arguments list for the named action (can only contain JSON valid data)
+ * @param {object} args The arguments for the named action
*/
- addRecipient(to: string | Uint8Array, action?: string, args?: any[]) {
+ addRecipient(to: string | Uint8Array, action: string, args?: object) {
const address = maybeHexToUint8Array(to);
- if (action && typeof action != "string") {
+ if (typeof action != "string") {
throw new Error("`action` must be a string");
}
- if (args && !Array.isArray(args)) {
- throw new Error("`args` must be an array");
+ if (args && typeof args !== "object") {
+ throw new Error("`args` must be an object");
}
- if (action && !args) {
- args = [];
+ if (!args) {
+ args = {};
}
this.data.recipients.push({ address, action, args });
@@ -277,17 +274,17 @@ export default class TransactionBuilder {
if (!Object.keys(Curve).includes(curve)) {
throw new Error(
"Curve must be one of " +
- Object.keys(Curve)
- .map((t) => `'${t}'`)
- .join(", ")
+ Object.keys(Curve)
+ .map((t) => `'${t}'`)
+ .join(", ")
);
}
if (!Object.keys(HashAlgorithm).includes(hashAlgo)) {
throw new Error(
"Hash algorithm must be one of " +
- Object.keys(HashAlgorithm)
- .map((t) => `'${t}'`)
- .join(", ")
+ Object.keys(HashAlgorithm)
+ .map((t) => `'${t}'`)
+ .join(", ")
);
}
@@ -338,7 +335,17 @@ export default class TransactionBuilder {
* Generate the payload for the previous signature by encoding address, type and data
*/
previousSignaturePayload() {
- const bufCodeSize = intToUint32Array(this.data.code.length);
+ let bufContract: Uint8Array;
+ if (this.data.contract != undefined) {
+ bufContract = concatUint8Arrays(
+ intToUint8Array(1),
+ intToUint32Array(this.data.contract.bytecode.byteLength),
+ this.data.contract.bytecode,
+ TE.serialize(this.data.contract.manifest)
+ )
+ } else {
+ bufContract = intToUint8Array(0)
+ }
let contentSize = this.data.content.length;
@@ -359,11 +366,11 @@ export default class TransactionBuilder {
return concatUint8Arrays(intToUint32Array(secret.byteLength), secret, concatUint8Arrays(...authorizedKeysBuffer));
});
- const ucoTransfersBuffers = this.data.ledger.uco.transfers.map(function (transfer) {
+ const ucoTransfersBuffers = this.data.ledger.uco.transfers.map(function(transfer) {
return concatUint8Arrays(transfer.to, intToUint64Array(transfer.amount));
});
- const tokenTransfersBuffers = this.data.ledger.token.transfers.map(function (transfer) {
+ const tokenTransfersBuffers = this.data.ledger.token.transfers.map(function(transfer) {
const bufTokenId = intToUint8Array(transfer.tokenId);
return concatUint8Arrays(
transfer.tokenAddress,
@@ -375,30 +382,19 @@ export default class TransactionBuilder {
});
const recipientsBuffer = this.data.recipients.map(({ address, action, args }) => {
- if (action == undefined || args == undefined) {
- return concatUint8Arrays(
- // 0 = unnamed action
- Uint8Array.from([0]),
- // address
- address
- );
- } else {
- const serializedArgs = args.map((arg) => TE.serialize(arg));
-
- return concatUint8Arrays(
- // 1 = named action
- Uint8Array.from([1]),
- // address
- address,
- // action
- Uint8Array.from([action.length]),
- new TextEncoder().encode(action),
- // args count
- Uint8Array.from([serializedArgs.length]),
- // args
- ...serializedArgs
- );
- }
+ const serializedArgs = TE.serialize(args);
+
+ return concatUint8Arrays(
+ // 1 = named action
+ intToUint8Array(1),
+ // address
+ address,
+ // action
+ Uint8Array.from([action.length]),
+ new TextEncoder().encode(action),
+ // args
+ serializedArgs
+ );
});
const bufOwnershipLength = intToUint8Array(this.data.ownerships.length);
@@ -410,10 +406,9 @@ export default class TransactionBuilder {
intToUint32Array(VERSION),
this.address,
Uint8Array.from([getTransactionTypeId(this.type)]),
- bufCodeSize,
- this.data.code,
+ bufContract,
bufContentSize,
- this.data.content,
+ new TextEncoder().encode(this.data.content),
Uint8Array.from([bufOwnershipLength.length]),
bufOwnershipLength,
...ownershipsBuffer,
@@ -432,14 +427,17 @@ export default class TransactionBuilder {
/**
* JSON RPC API SEND_TRANSACTION
*/
- toNodeRPC(): TransactionRPC {
+ async toNodeRPC(): Promise {
return {
version: this.version,
address: uint8ArrayToHex(this.address),
type: this.type,
data: {
- content: new TextDecoder().decode(this.data.content),
- code: new TextDecoder().decode(this.data.code),
+ content: this.data.content,
+ contract: this.data.contract ? {
+ bytecode: uint8ArrayToHex(this.data.contract?.bytecode),
+ manifest: this.data.contract?.manifest
+ } : undefined,
ownerships: this.data.ownerships.map(({ secret, authorizedPublicKeys }) => {
return {
secret: uint8ArrayToHex(secret),
@@ -496,8 +494,8 @@ export default class TransactionBuilder {
version: this.version,
type: this.type,
data: {
- content: new TextDecoder().decode(this.data.content),
- code: new TextDecoder().decode(this.data.code),
+ content: this.data.content,
+ contract: this.data.contract,
ownerships: this.data.ownerships.map(({ secret, authorizedPublicKeys }) => {
return {
secret: uint8ArrayToHex(secret),
diff --git a/src/types.ts b/src/types.ts
index 525aa35..dc423f7 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,3 +1,5 @@
+import { Contract, ContractManifest } from "./contract";
+
export enum Curve {
ed25519 = "ed25519",
P256 = "P256",
@@ -76,42 +78,40 @@ type CrossValidationStamp = {
};
export type TransactionData = {
- code: Uint8Array;
- content: Uint8Array;
+ contract?: Contract;
+ content: string;
ledger: Ledger;
ownerships: Ownership[];
recipients: Recipient[];
};
type Ledger = {
- token: TokenLedger;
uco: UcoLedger;
+ token: TokenLedger;
};
-type TokenLedger = {
- transfers: TokenTransfer[];
+type UcoLedger = {
+ transfers: Transfer[];
};
-type TokenTransfer = {
+type Transfer = {
amount: bigint;
to: Uint8Array;
- tokenAddress: Uint8Array;
- tokenId: number;
};
-type UcoLedger = {
- transfers: UcoTransfer[];
+type TokenLedger = {
+ transfers: TokenTransfer[];
};
-type UcoTransfer = {
- amount: bigint;
- to: Uint8Array;
-};
+type TokenTransfer = {
+ tokenAddress: Uint8Array;
+ tokenId: number;
+} & Transfer;
export type Recipient = {
address: Uint8Array;
- action?: string;
- args?: any[];
+ action: string;
+ args: object;
};
export type Ownership = {
@@ -190,19 +190,17 @@ export type Keypair = {
privateKey: Uint8Array;
};
-type Transfer = {
+export type TransferRPC = {
to: string;
amount: bigint;
};
-type TokenTransferRPC = {
- to: string;
- amount: bigint;
+export type TokenTransferRPC = {
tokenAddress: string;
tokenId: number;
-};
+} & TransferRPC;
-type OwnershipRPC = {
+export type OwnershipRPC = {
secret: string;
authorizedKeys: {
publicKey: string;
@@ -210,23 +208,28 @@ type OwnershipRPC = {
}[];
};
-type RecipientRPC = {
+export type RecipientRPC = {
address: string;
action?: string;
- args?: any[];
+ args?: any[] | object;
};
+export type ContractRPC = {
+ bytecode: string;
+ manifest: ContractManifest;
+}
+
export type TransactionRPC = {
version: number;
address: string;
type: UserTypeTransaction;
data: {
content: string;
- code: string;
+ contract?: ContractRPC;
ownerships: OwnershipRPC[];
ledger: {
uco: {
- transfers: Transfer[];
+ transfers: TransferRPC[];
};
token: {
transfers: TokenTransferRPC[];
diff --git a/tests/account.test.ts b/tests/account.test.ts
index 87f4073..0a8b597 100644
--- a/tests/account.test.ts
+++ b/tests/account.test.ts
@@ -20,7 +20,7 @@ describe("Account", () => {
const tx = account.newKeychainTransaction(expectedKeychain, 0);
expect(tx.type).toBe("keychain");
- expect(new TextDecoder().decode(tx.data.content)).toBe(JSON.stringify(expectedKeychain.toDID()));
+ expect(tx.data.content).toBe(JSON.stringify(expectedKeychain.toDID()));
expect(tx.data.ownerships.length).toBe(1);
diff --git a/tests/network.test.ts b/tests/network.test.ts
index d7b16e6..686e372 100644
--- a/tests/network.test.ts
+++ b/tests/network.test.ts
@@ -103,7 +103,7 @@ describe("Network", () => {
const tx = archethic.transaction.new();
tx.setType("data");
tx.setContent("content");
- tx.addRecipient("0000EE9DDC5229EBFFE197277058F11A41E22252D86A904C8CBCF38C1EFC42AB5065");
+ tx.addRecipient("0000EE9DDC5229EBFFE197277058F11A41E22252D86A904C8CBCF38C1EFC42AB5065", "action");
nock("http://127.0.0.1:4000", {
reqheaders: {
@@ -116,7 +116,7 @@ describe("Network", () => {
id: 1,
method: "estimate_transaction_fee",
params: {
- transaction: tx.toNodeRPC()
+ transaction: await tx.toNodeRPC()
}
})
// @ts-ignore
@@ -142,7 +142,7 @@ describe("Network", () => {
const tx = archethic.transaction.new();
tx.setType("data");
tx.setContent("content");
- tx.addRecipient("0000EE9DDC5229EBFFE197277058F11A41E22252D86A904C8CBCF38C1EFC42AB5065");
+ tx.addRecipient("0000EE9DDC5229EBFFE197277058F11A41E22252D86A904C8CBCF38C1EFC42AB5065", "action");
nock("http://127.0.0.1:4000", {
reqheaders: {
@@ -155,7 +155,7 @@ describe("Network", () => {
id: 1,
method: "send_transaction",
params: {
- transaction: tx.toNodeRPC()
+ transaction: await tx.toNodeRPC()
}
})
// @ts-ignore
@@ -177,8 +177,8 @@ describe("Network", () => {
const tx = archethic.transaction.new();
tx.setType("data");
tx.setContent("content");
- tx.addRecipient("0000EE9DDC5229EBFFE197277058F11A41E22252D86A904C8CBCF38C1EFC42AB5065");
- tx.addRecipient("0000EE9DDC5229EBFFE197277058F11A41E22252D86A904C8CBCF38C1EFC42AB5064");
+ tx.addRecipient("0000EE9DDC5229EBFFE197277058F11A41E22252D86A904C8CBCF38C1EFC42AB5065", "action");
+ tx.addRecipient("0000EE9DDC5229EBFFE197277058F11A41E22252D86A904C8CBCF38C1EFC42AB5064", "action");
nock("http://127.0.0.1:4000", {
reqheaders: {
@@ -191,7 +191,7 @@ describe("Network", () => {
id: 1,
method: "simulate_contract_execution",
params: {
- transaction: tx.toNodeRPC()
+ transaction: await tx.toNodeRPC()
}
})
// @ts-ignore
diff --git a/tests/transaction_builder.test.ts b/tests/transaction_builder.test.ts
index 68d7484..bddbcf8 100644
--- a/tests/transaction_builder.test.ts
+++ b/tests/transaction_builder.test.ts
@@ -1,9 +1,9 @@
-import TransactionBuilder from "../src/transaction_builder";
-import { deriveAddress, deriveKeyPair, sign, verify } from "../src/crypto";
-import { concatUint8Arrays, hexToUint8Array, intToUint32Array, intToUint64Array, parseBigInt } from "../src/utils";
+import TransactionBuilder, { VERSION } from "../src/transaction_builder";
+import { deriveAddress, deriveKeyPair, verify } from "../src/crypto";
+import { concatUint8Arrays, hexToUint8Array, intToUint32Array, intToUint64Array, intToUint8Array, parseBigInt } from "../src/utils";
import TE from "../src/typed_encoding";
-
-const VERSION = 3;
+import { Contract } from "../src/contract";
+import typed_encoding from "../src/typed_encoding";
// all assert should be transformed to jest expect
describe("Transaction builder", () => {
@@ -40,17 +40,18 @@ describe("Transaction builder", () => {
});
});
- describe("setCode", () => {
+ describe("setContract", () => {
it("should insert the code into the transaction data", () => {
- const tx = new TransactionBuilder("transfer").setCode("my smart contract code");
- expect(new TextDecoder().decode(tx.data.code)).toBe("my smart contract code");
+ const contract = new Contract(new Uint8Array(), { abi: { state: {}, functions: {} } })
+ const tx = new TransactionBuilder("transfer").setContract(contract)
+ expect(tx.data.contract?.bytecode).toStrictEqual(contract.bytecode);
});
});
describe("setContent", () => {
it("should insert the content into the transaction data", () => {
const tx = new TransactionBuilder("transfer").setContent("my super content");
- expect(tx.data.content).toStrictEqual(new TextEncoder().encode("my super content"));
+ expect(tx.data.content).toBe("my super content");
});
});
@@ -118,16 +119,22 @@ describe("Transaction builder", () => {
describe("addRecipient", () => {
it("should add a recipient for named action", () => {
- const tx = new TransactionBuilder("transfer").addRecipient(
- "0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646",
- "vote",
- ["Miles"]
- );
+ const tx = new TransactionBuilder("transfer")
+ .addRecipient(
+ "0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646",
+ "vote",
+ { name: "Miles" }
+ )
+ .addRecipient("0000ae12908c889ba2728614abc6ae472ed9f1df0b3afefd1faa290473a47ceb42bd", "addHero", { name: "John Doe" })
- expect(tx.data.recipients.length).toBe(1);
+ expect(tx.data.recipients.length).toBe(2);
expect(tx.data.recipients[0].action).toBe("vote");
- expect(tx.data.recipients[0].args!.length).toBe(1);
- expect(tx.data.recipients[0].args![0]).toBe("Miles");
+
+ const objectArg1 = tx.data.recipients[0].args as { name: string }
+ expect(objectArg1.name).toBe("Miles");
+
+ const objectArg2 = tx.data.recipients[1].args as { name: string }
+ expect(objectArg2.name).toBe("John Doe");
});
it("should throw if types are incorrect", () => {
@@ -142,16 +149,12 @@ describe("Transaction builder", () => {
describe("previousSignaturePayload", () => {
it("should generate binary encoding of the transaction before signing", () => {
- const code = `
- condition inherit: [
- uco_transferred: 0.020
- ]
-
- actions triggered by: transaction do
- set_type transfer
- add_uco_ledger to: "000056E763190B28B4CF9AAF3324CF379F27DE9EF7850209FB59AA002D71BA09788A", amount: 0.020
- end
- `;
+ const contract = new Contract(new Uint8Array(5), {
+ abi: {
+ state: { "value": "u32" },
+ functions: {}
+ }
+ })
const content =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sit amet leo egestas, lobortis lectus a, dignissim orci.";
@@ -171,9 +174,10 @@ describe("Transaction builder", () => {
parseBigInt("100"),
"0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88"
)
- .setCode(code)
+ .setContract(contract)
.setContent(content)
- .addRecipient("0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88");
+ .addRecipient("0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88", "action")
+ .addRecipient("0000ae12908c889ba2728614abc6ae472ed9f1df0b3afefd1faa290473a47ceb42bd", "addHero", { name: "John Doe" });
const keypair = deriveKeyPair("seed", 0);
@@ -187,9 +191,12 @@ describe("Transaction builder", () => {
intToUint32Array(VERSION),
tx.address,
Uint8Array.from([253]),
- //Code size
- intToUint32Array(code.length),
- new TextEncoder().encode(code),
+ //Contract is filled
+ intToUint8Array(1),
+ //Contract bytecode size
+ intToUint32Array(contract.bytecode.length),
+ contract.bytecode,
+ typed_encoding.serialize(contract.manifest),
//Content size
intToUint32Array(content.length),
new TextEncoder().encode(content),
@@ -231,128 +238,28 @@ describe("Transaction builder", () => {
// Nb of byte to encode nb of recipients
Uint8Array.from([1]),
// Nb of recipients
+ Uint8Array.from([2]),
+ // 0 = named recipient
Uint8Array.from([1]),
- // 0 = unnamed recipient
- Uint8Array.from([0]),
- hexToUint8Array("0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88")
+ hexToUint8Array("0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88"),
+ Uint8Array.from([6]),
+ new TextEncoder().encode("action"),
+ TE.serialize({}),
+ // 1 = named recipient
+ Uint8Array.from([1]),
+ hexToUint8Array("0000ae12908c889ba2728614abc6ae472ed9f1df0b3afefd1faa290473a47ceb42bd"),
+ Uint8Array.from([7]),
+ new TextEncoder().encode("addHero"),
+ TE.serialize({ name: "John Doe" })
);
expect(payload).toEqual(expected_binary);
});
- // it("should generate binary encoding of the transaction before signing with named action", () => {
- // const code = `
- // condition inherit: [
- // uco_transferred: 0.020
- // ]
-
- // actions triggered by: transaction do
- // set_type transfer
- // add_uco_ledger to: "000056E763190B28B4CF9AAF3324CF379F27DE9EF7850209FB59AA002D71BA09788A", amount: 0.020
- // end
- // `;
-
- // const content =
- // "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sit amet leo egestas, lobortis lectus a, dignissim orci.";
-
- // const secret = "mysecret";
-
- // const tx = new TransactionBuilder("transfer")
- // .addOwnership(secret, [
- // {
- // publicKey: "0001b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646",
- // encryptedSecretKey: "00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88"
- // }
- // ])
- // .addUCOTransfer("0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", parseBigInt("0.202"))
- // .addTokenTransfer(
- // "0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646",
- // parseBigInt("100"),
- // "0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88"
- // )
- // .setCode(code)
- // .setContent(content)
- // .addRecipient("0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88", "vote_for_mayor", [
- // "Ms. Smith"
- // ]);
-
- // const keypair = deriveKeyPair("seed", 0);
-
- // tx.address = deriveAddress("seed", 1);
- // tx.previousPublicKey = keypair.publicKey;
-
- // const payload = tx.previousSignaturePayload();
-
- // const expected_binary = concatUint8Arrays(
- // //Version
- // intToUint32Array(VERSION),
- // tx.address,
- // Uint8Array.from([253]),
- // //Code size
- // intToUint32Array(code.length),
- // new TextEncoder().encode(code),
- // //Content size
- // intToUint32Array(content.length),
- // new TextEncoder().encode(content),
- // // Nb of byte to encode nb of ownerships
- // Uint8Array.from([1]),
- // //Nb of ownerships
- // Uint8Array.from([1]),
- // //Secret size
- // intToUint32Array(secret.length),
- // new TextEncoder().encode(secret),
- // // Nb of byte to encode nb of authorized keys
- // Uint8Array.from([1]),
- // // Nb of authorized keys
- // Uint8Array.from([1]),
- // // Authorized keys encoding
- // concatUint8Arrays(
- // hexToUint8Array("0001b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646"),
- // hexToUint8Array("00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88")
- // ),
- // // Nb of byte to encode nb of uco transfers
- // Uint8Array.from([1]),
- // // Nb of uco transfers
- // Uint8Array.from([1]),
- // concatUint8Arrays(
- // hexToUint8Array("0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646"),
- // intToUint8Array(parseBigInt("0.202"))
- // ),
- // // Nb of byte to encode nb of Token transfers
- // Uint8Array.from([1]),
- // // Nb of Token transfers
- // Uint8Array.from([1]),
- // concatUint8Arrays(
- // hexToUint8Array("0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88"),
- // hexToUint8Array("0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646"),
- // intToUint8Array(parseBigInt("100")),
- // Uint8Array.from([1]),
- // Uint8Array.from([0])
- // ),
- // // Nb of byte to encode nb of recipients
- // Uint8Array.from([1]),
- // // Nb of recipients
- // Uint8Array.from([1]),
- // // 1 = named recipient
- // Uint8Array.from([1]),
- // hexToUint8Array("0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88"),
- // // action
- // // action size on 1 byte
- // Uint8Array.from([14]),
- // // action value
- // new TextEncoder().encode("vote_for_mayor"),
- // // args size
- // Uint8Array.from([1]),
- // // args value
- // TE.serialize("Ms. Smith")
- // );
- // expect(payload).toEqual(expected_binary);
- // });
-
it("should order the keys or named action args in the generated binary", () => {
const tx = new TransactionBuilder("transfer").addRecipient(
"0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88",
"set_geopos",
- [{ lng: 2, lat: 1 }]
+ { lng: 2, lat: 1 }
);
const keypair = deriveKeyPair("seed", 0);
@@ -367,8 +274,8 @@ describe("Transaction builder", () => {
intToUint32Array(VERSION),
tx.address,
Uint8Array.from([253]),
- //Code size
- intToUint32Array(0),
+ //No contract
+ intToUint8Array(0),
//Content size
intToUint32Array(0),
// Nb of byte to encode nb of ownerships
@@ -395,8 +302,6 @@ describe("Transaction builder", () => {
Uint8Array.from([10]),
// action value
new TextEncoder().encode("set_geopos"),
- // args size
- Uint8Array.from([1]),
// args value
TE.serialize({ lng: 2, lat: 1 })
);
@@ -445,10 +350,10 @@ describe("Transaction builder", () => {
expect(txRPC).toHaveProperty("generateEncryptedSeedSC", true);
});
- it("should not affect the NodeTransactionRPC", () => {
+ it("should not affect the NodeTransactionRPC", async () => {
const generateEncryptedSeedSC = true;
const tx = new TransactionBuilder("transfer").setGenerateEncryptedSeedSC(generateEncryptedSeedSC);
- const txRPC = tx.toNodeRPC();
+ const txRPC = await tx.toNodeRPC();
expect(txRPC).not.toHaveProperty("generateEncryptedSeedSC", true);
});
@@ -470,110 +375,6 @@ describe("Transaction builder", () => {
});
});
- // describe("originSignaturePayload", () => {
- // it("should generate binary encoding of the transaction before signing", () => {
- // const secret = "mysecret";
- // const content =
- // "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sit amet leo egestas, lobortis lectus a, dignissim orci.";
- // const code = `condition inherit: [
- // uco_transferred: 0.020
- // ]
-
- // actions triggered by: transaction do
- // set_type transfer
- // add_uco_ledger to: "000056E763190B28B4CF9AAF3324CF379F27DE9EF7850209FB59AA002D71BA09788A", amount: 0.020
- // end
- // `;
-
- // const tx = new TransactionBuilder("transfer")
- // .addOwnership(secret, [
- // {
- // publicKey: "0001b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646",
- // encryptedSecretKey: "00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88"
- // },
- // {
- // publicKey: "0001a1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646",
- // encryptedSecretKey: "00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88"
- // }
- // ])
- // .addUCOTransfer("0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646", parseBigInt("0.202"))
- // .addTokenTransfer(
- // "0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646",
- // parseBigInt("100"),
- // "0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88"
- // )
- // .setCode(code)
- // .setContent(content)
- // .addRecipient("0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88")
- // .build("seed", 0, "P256");
-
- // const transactionKeyPair = deriveKeyPair("seed", 0, Curve.P256);
- // const previousSig = sign(tx.previousSignaturePayload(), transactionKeyPair.privateKey);
-
- // const payload = tx.originSignaturePayload();
- // const expected_binary = concatUint8Arrays(
- // //Version
- // intToUint32Array(VERSION),
- // tx.address,
- // Uint8Array.from([253]),
- // //Code size
- // intToUint32Array(code.length),
- // new TextEncoder().encode(code),
- // //Content size
- // intToUint32Array(content.length),
- // new TextEncoder().encode(content),
- // // Nb of byte to encode nb of ownerships
- // Uint8Array.from([1]),
- // //Nb ownerships
- // Uint8Array.from([1]),
- // //Secret size
- // intToUint32Array(secret.length),
- // new TextEncoder().encode(secret),
- // // Nb of byte to encode nb of authorized key
- // Uint8Array.from([1]),
- // // Nb of authorized keys
- // Uint8Array.from([2]),
- // // Authorized keys encoding
- // concatUint8Arrays(
- // hexToUint8Array("0001a1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646"),
- // hexToUint8Array("00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88"),
- // hexToUint8Array("0001b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646"),
- // hexToUint8Array("00501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88")
- // ),
- // // Nb of byte to encode nb of uco transfers
- // Uint8Array.from([1]),
- // // Nb of uco transfers
- // Uint8Array.from([1]),
- // concatUint8Arrays(
- // hexToUint8Array("0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646"),
- // intToUint8Array(parseBigInt("0.202"))
- // ),
- // // Nb of byte to encode nb of Token transfers
- // Uint8Array.from([1]),
- // // Nb of Token transfers
- // Uint8Array.from([1]),
- // concatUint8Arrays(
- // hexToUint8Array("0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88"),
- // hexToUint8Array("0000b1d3750edb9381c96b1a975a55b5b4e4fb37bfab104c10b0b6c9a00433ec4646"),
- // intToUint8Array(parseBigInt("100")),
- // Uint8Array.from([1]),
- // Uint8Array.from([0])
- // ),
- // // Nb of byte to encode nb of recipients
- // Uint8Array.from([1]),
- // // Nb of recipients
- // Uint8Array.from([1]),
- // // 0 = unnamed recipient
- // Uint8Array.from([0]),
- // hexToUint8Array("0000501fa2db78bcf8ceca129e6139d7e38bf0d61eb905441056b9ebe6f1d1feaf88"),
- // transactionKeyPair.publicKey,
- // Uint8Array.from([previousSig.length]),
- // previousSig
- // );
- // expect(payload).toStrictEqual(expected_binary);
- // });
- // });
-
describe("originSign", () => {
it("should sign the transaction with a origin private key", () => {
const originKeypair = deriveKeyPair("origin_seed", 0);