Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add example contracts showcase #294

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
47 changes: 47 additions & 0 deletions .github/workflows/contract-showcase-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Contract Showcase CI

on:
pull_request:
paths:
- contracts_showcase/**

defaults:
run:
working-directory: ./contracts_showcase/

jobs:
collect_dirs:
runs-on: ubuntu-latest
outputs:
dirs: ${{ steps.dirs.outputs.dirs }}
steps:
- uses: actions/checkout@v2
- id: dirs
run: echo "dirs=$(ls -d ./*/ | jq --raw-input --slurp --compact-output 'split("\n")[:-1]')" >> ${GITHUB_OUTPUT}

run_tests:
needs: collect_dirs
runs-on: ubuntu-latest
strategy:
matrix:
dir: ${{ fromJson(needs.collect_dirs.outputs.dirs) }}
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Install node
uses: actions/setup-node@v3
with:
node-version: "18"
cache: "npm"

- name: Install dependencies
working-directory: ./contracts_showcase/${{ matrix.dir }}
run: npm install

- name: Test
working-directory: ./contracts_showcase/${{ matrix.dir }}
run: npm run start
env:
JSON_RPC_URL_PUBLIC: ${{ secrets.JSON_RPC_URL_PUBLIC }}
WALLET_SECRET_KEY: ${{ secrets.WALLET_PRIVATE_KEY }}
7 changes: 7 additions & 0 deletions contracts_showcase/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## How to add a new contract showcase

Copy/Paste one of the sub-folder here.

In the `assembly/contracts` copy the source code of your contract and in `interaction.ts`, deploy your contract and make some possible interactions with it using massa-web3. You can use the examples from the other sub-folder to create yours.

Try trigger
7 changes: 7 additions & 0 deletions contracts_showcase/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## Contracts showcase

In this section you will find examples of smart contracts showcasing usage of objects available in the `massa-as-sdk` package.

Each of the examples has a `assembly/contracts` folder containing the smart contract codes and a `interaction.ts` file that contains all the deployment and interaction with the smart contract.

You can go to any folder copy the `.env.example` to a `.env` add your secret key, update the RPC if you don't want to use buildnet and run `npm run start`. It will run all the actions in the `interaction.ts`
2 changes: 2 additions & 0 deletions contracts_showcase/accesscontrol/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
WALLET_SECRET_KEY=
JSON_RPC_URL_PUBLIC=https://buildnet.massa.net/api/v2
2 changes: 2 additions & 0 deletions contracts_showcase/accesscontrol/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env
node_modules/
21 changes: 21 additions & 0 deletions contracts_showcase/accesscontrol/asconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"targets": {
"debug": {
"outFile": "build/main.wasm",
"sourceMap": true,
"debug": true
},
"release": {
"outFile": "build/main.wasm",
"sourceMap": true,
"optimizeLevel": 3,
"shrinkLevel": 0,
"converge": false,
"noAssert": false
}
},
"options": {
"exportRuntime": true,
"bindings": "esm"
}
}
41 changes: 41 additions & 0 deletions contracts_showcase/accesscontrol/assembly/contracts/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Args, stringToBytes } from "@massalabs/as-types";
import {
Address,
Storage,
Context,
caller,
} from "@massalabs/massa-as-sdk";
import { AccessControl } from "@massalabs/massa-as-sdk/assembly/security";

const controller = new AccessControl<u8>(1);

const ADMIN = controller.newPermission("admin");
const USER = controller.newPermission("user");

export function constructor(argsSer: StaticArray<u8>): void {
if (!Context.isDeployingContract()) {
return;
}

const args = new Args(argsSer);
const admin = new Address(args.nextString().unwrap());
const user = new Address(args.nextString().unwrap());
controller.grantPermission(ADMIN, admin);
controller.grantPermission(USER, admin);
controller.grantPermission(USER, user);
Storage.set("admin", admin.toString());
}

export function changeAdmin(argsSer: StaticArray<u8>): void {
const args = new Args(argsSer);
const newAdmin = new Address(args.nextString().unwrap());
controller.mustHavePermission(ADMIN, caller());
controller.grantPermission(ADMIN, newAdmin);
controller.revokePermission(ADMIN, caller());
Storage.set("admin", newAdmin.toString());
}

export function getAdmin(): StaticArray<u8> {
controller.mustHaveAnyPermission(USER | ADMIN, caller());
return stringToBytes(Storage.get("admin"));
}
126 changes: 126 additions & 0 deletions contracts_showcase/accesscontrol/interaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import {
Account,
AccountOperation,
Args,
JsonRPCClient,
OperationStatus,
SmartContract,
bytesToStr,
} from "@massalabs/massa-web3";
import path from "path";
import { fileURLToPath } from "url";
import { readFileSync } from "fs";
import * as dotenv from "dotenv";

// Load environment variables from .env file
dotenv.config();

// // Helper function to read bytecode from file
function getByteCode(folderName: string, fileName: string): Buffer {
// Obtain the current file name and directory paths
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
return readFileSync(path.join(__dirname, folderName, fileName));
}

// // Helper function to get the address from the deployment result
// async function getEventsFromOp(opId: string): Promise<IEvent[]> {
// // Wait for the operation to be speculative success or Speculative failure
// // First promise that resolves stops the execution
// await web3Client
// .smartContracts()
// .awaitMultipleRequiredOperationStatus(opId, [
// EOperationStatus.SPECULATIVE_SUCCESS,
// EOperationStatus.SPECULATIVE_ERROR,
// ]);

// // Filter to get the deployment event
// const eventsFilter = {
// start: null,
// end: null,
// original_caller_address: null,
// original_operation_id: opId,
// emitter_address: null,
// is_final: false,
// } as IEventFilter;

// // Get the deployment event
// const deploymentEvents = await EventPoller.getEventsOnce(
// eventsFilter,
// web3Client
// );
// return deploymentEvents;
// }

// Get environment variables
const decimals = 10 ** 9;
const apiUrl = "https://buildnet.massa.net/api/v2";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is exported by massa web3


const adminAccount = await Account.fromEnv();
const adminAddress = adminAccount.address.toString();
const userAccount = await Account.generate();
const client = new JsonRPCClient(apiUrl);
const adminAccountOperation = await new AccountOperation(adminAccount, client);

adminAccountOperation.transfer(userAccount.address, BigInt(10 * decimals));

const contract = await SmartContract.deploy(
client,
adminAccount,
{
byteCode: getByteCode("build", "main.wasm"),
coins: BigInt(10 * decimals),
parameter: new Args()
.addString(adminAddress)
.addString(userAccount.address.toString())
.serialize(),
},
{
waitFinalExecution: false,
}
);

const adminAddressSC = bytesToStr(
(await contract.read("getAdmin", new Args().serialize())).value
);
if (adminAddressSC !== adminAddress) {
throw new Error("Admin address is not the expected one");
}
console.log("Admin address is the expected one");

console.log(
"Trying to change the admin address with the user account (not allowed)..."
);
try {
await contract.call(
"changeAdmin",
new Args().addString(userAccount.address.toString()).serialize(),
{
account: userAccount,
}
);
} catch {
console.log("Admin address change failed as expected");
}

console.log(
"Trying to change the admin address with the deployer account (allowed)..."
);
const op = await contract.call(
"changeAdmin",
new Args().addString(userAccount.address.toString()).serialize(),
{
account: adminAccount,
}
);
await op.waitSpeculativeExecution();
console.log("Admin address changed successfully");

const adminAddressSC2 = bytesToStr(
(await contract.read("getAdmin", new Args().serialize())).value
);
if (adminAddressSC2 !== userAccount.address.toString()) {
throw new Error("Admin address is not the expected one");
}

process.exit(0);
Loading
Loading