Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
1a27a1f
up node version
samuelmanzanera Aug 30, 2024
6b55574
Update contract function detection
samuelmanzanera Aug 30, 2024
58bdabf
Support compressed contract code in tx builder
samuelmanzanera Aug 30, 2024
204fe6c
update example transaction builder
samuelmanzanera Aug 30, 2024
aafae0d
update
samuelmanzanera Sep 6, 2024
93a1de9
use manifest to contract
samuelmanzanera Sep 11, 2024
daccfed
fix null contract arguments
samuelmanzanera Oct 1, 2024
82fe981
Support object for recipient arg
samuelmanzanera Oct 4, 2024
33f6dcf
change tx structure to add contract object
samuelmanzanera Oct 4, 2024
ef3b85b
use typedencoding to serialize contract manfiest
samuelmanzanera Oct 8, 2024
a870368
use pako to compress contract
samuelmanzanera Oct 8, 2024
4fefe14
improve types with CryptoJS
samuelmanzanera Oct 8, 2024
77f5e72
update tx builder to support wasm
samuelmanzanera Oct 11, 2024
7ad11e5
lint
samuelmanzanera Oct 11, 2024
b6ede2a
improve error logging in tx builder example
samuelmanzanera Dec 9, 2024
e5ace85
fix code serialization issue
samuelmanzanera Dec 20, 2024
c8008e1
Update serialization, set recipient args only object
Neylix Dec 27, 2024
a5c8666
Update readme
Neylix Dec 27, 2024
edf9541
2.0.0-rc.0
Neylix Dec 27, 2024
8dc8d8b
Remove unused class
Neylix Jan 9, 2025
fb1fb10
typo
Neylix Jan 9, 2025
e627039
Remove useless variable
samuelmanzanera Mar 11, 2025
c40c5e4
Handle partial tx data for smart contract transaction
samuelmanzanera Mar 11, 2025
8d847ca
2.0.0-rc.1
samuelmanzanera Mar 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:

strategy:
matrix:
node-version: [16.x]
node-version: [20.x]

steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1 @@
nodejs 18.12.1
nodejs 20.17.0
102 changes: 92 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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";
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1375,15 +1419,31 @@ tx.originSign(originPrivateKey)

<br />

### 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)
Expand All @@ -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();
```

</details>

Expand Down
79 changes: 64 additions & 15 deletions example/transactionBuilder/app.js
Original file line number Diff line number Diff line change
@@ -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 = [];
Expand Down Expand Up @@ -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;
});
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
Expand All @@ -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");
Expand All @@ -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);
Expand All @@ -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 = "<option></option>";
Expand Down Expand Up @@ -355,7 +384,7 @@ window.sendTransaction = async () => {
if (error.data.data) {
errMsg += `<p style="padding-left: 20px">(${error.data.data.code}) ${error.data.data.message}</p>`;
} else {
errMsg += `<p style="padding-left: 20px">${error.data.message}</p>`;
errMsg += `<p style="padding-left: 20px">${JSON.stringify(error.data.message)}</p>`;
}
}

Expand Down Expand Up @@ -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;

Expand Down
17 changes: 12 additions & 5 deletions example/transactionBuilder/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,21 @@ <h1 class="is-size-5 has-text-white">Transaction Builder</h1>
<option value="token">Token</option>
<option value="data">Data</option>
<option value="contract">Contract</option>
<option value="code_proposal">Code Proposal</option>
<option value="code_approval">Code Approval</option>
<!-- <option value="code_proposal">Code Proposal</option>
<option value="code_approval">Code Approval</option> -->
</select>
</div>
<div class="box">
<label for="code">Enter your smart contract code</label>
<br />
<textarea id="code" cols="40" rows="5" class="textarea" placeholder="Smart contract code"></textarea>
<div class="columns">
<div class="column">
<label for="bytecode_upload">Choose your WASM smart contract bytecode</label>
<input type="file" id="bytecode_upload" />
</div>
<div class="column">
<label for="manifest_upload">Choose the manifest associated</label>
<input type="file" id="manifest_upload" />
</div>
</div>
</div>
<div class="box">
<label for="code">Enter your content</label>
Expand Down
Loading