Skip to content

Commit

Permalink
added interaction scripts - 1
Browse files Browse the repository at this point in the history
  • Loading branch information
alexbolog committed Sep 15, 2023
1 parent d9e3c85 commit b67aeb2
Show file tree
Hide file tree
Showing 6 changed files with 1,665 additions and 0 deletions.
19 changes: 19 additions & 0 deletions scripts/.env.default
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
ENV="devnet"
# full path to the pem file
# e.g. "/home/user/my_wallet.pem"
PEM_mainnet=""
PEM_testnet=""
PEM_devnet=""

NETWORKS_DEVNET_CHAIN=D
NETWORKS_DEVNET_GATEWAY=https://devnet-gateway.multiversx.com
NETWORKS_DEVNET_API=https://devnet-api.multiversx.com
NETWORKS_TESTNET_CHAIN=T
NETWORKS_TESTNET_GATEWAY=https://testnet-gateway.multiversx.com
NETWORKS_TESTNET_API=https://testnet-api.multiversx.com
NETWORKS_MAINNET_CHAIN=1
NETWORKS_MAINNET_GATEWAY=https://gateway.multiversx.com
NETWORKS_MAINNET_API=https://api.multiversx.com

SC_ABI_FILE_PATH="../output/nft-staking.abi.json"
SC_WASM_FILE_PATH="../output/nft-staking.wasm"
217 changes: 217 additions & 0 deletions scripts/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
require("dotenv").config();
const { promises } = require("fs");
const {
ApiNetworkProvider,
ProxyNetworkProvider,
} = require("@multiversx/sdk-network-providers");
const { parseUserKey, UserSigner } = require("@multiversx/sdk-wallet");
const {
Account,
ResultsParser,
AbiRegistry,
SmartContract,
Address,
TransactionWatcher,
Code,
CodeMetadata,
TokenIdentifierValue,
} = require("@multiversx/sdk-core");
const config = require("./config.json");

// load pem content and account
// sign and send tx
// automatic load network and pem files
// provide deploy, upgrade functions
const loadNetworkConfig = () => {
const workingEnv = process.env.ENVIRONMENT;
const chainId = process.env[`NETWORKS_${workingEnv}_CHAIN`];
const gateway = process.env[`NETWORKS_${workingEnv}_GATEWAY`];
const api = process.env[`NETWORKS_${workingEnv}_API`];
const pem = process.env[`PEM_${workingEnv}`];

return {
chain: chainId,
gateway,
api,
pem,
};
};

const networkCfg = loadNetworkConfig();

const getPemAndAccount = async () => {
console.log(`Loading pem and account for ${process.env.ENVIRONMENT}...`);
console.log(`PEM Path: ${networkCfg.pem}`);
const apiProvider = new ApiNetworkProvider(networkCfg.api);
const pemContent = await loadPemContent(networkCfg.pem);
const account = await loadUserAccount(apiProvider, pemContent);
return {
pem: pemContent,
account,
};
};

const loadPemContent = async (path) => {
let buffer = await promises.readFile(path);
return buffer.toString();
};

const loadUserAccount = async (apiProvider, walletPemContents) => {
const userKey = parseUserKey(walletPemContents);
const address = userKey.generatePublicKey().toAddress();

const account = new Account(address);
const apiAccount = await apiProvider.getAccount(address);
account.update(apiAccount);
return account;
};

const Parser = new ResultsParser();

const getSmartContract = async () => {
const scAddress = config.address[process.env.ENVIRONMENT];
const abiJson = await promises.readFile(process.env.SC_ABI_FILE_PATH, {
encoding: "utf8",
});
const abiObj = JSON.parse(abiJson);
const abiRegistry = AbiRegistry.create(abiObj);
return new SmartContract({
address: new Address(scAddress),
abi: abiRegistry,
});
};

const signAndSend = async (tx, walletPemContents) => {
const provider = getProxyProvider();
const signer = prepareUserSigner(walletPemContents);
const serializedTransaction = tx.serializeForSigning();
const signature = await signer.sign(serializedTransaction);
tx.applySignature(signature);
await provider.sendTransaction(tx);
console.log(`Transaction sent. Tx hash: ${tx.getHash().toString()}`);
const watcher = new TransactionWatcher(provider);
const transactionOnNetwork = await watcher.awaitCompleted(tx);
return transactionOnNetwork;
};

const prepareUserSigner = (walletPemContents) => {
return UserSigner.fromPem(walletPemContents);
};

const getProxyProvider = () => {
return new ProxyNetworkProvider(networkCfg.gateway);
};

const createCodeMetadata = (payable, payableBySc) => {
return new CodeMetadata(true, true, payable, payableBySc);
};

const loadWasm = async () => {
let buffer = await promises.readFile(process.env.SC_WASM_FILE_PATH);
let code = Code.fromBuffer(buffer);
return code;
};

const deploy = async () => {
let contract = new SmartContract();
let { pem, account } = await getPemAndAccount();
let code = await loadWasm();
let codeMetadata = createCodeMetadata(
config.deploymentArgs.payable,
config.deploymentArgs.payableBySc
);

const transaction = contract.deploy({
deployer: account.address,
code: code,
codeMetadata: codeMetadata,
initArguments: buildDeployArgs(),
gasLimit: config.deploymentArgs.gasLimit,
chainID: networkCfg.chain,
});
transaction.setNonce(account.getNonceThenIncrement());

console.log(`Deploying contract on ${process.env.ENVIRONMENT}...`);
const txResult = await signAndSend(transaction, pem);
const deployedAddress = deploymentTransactionResultHandler(txResult);

if (deployedAddress !== "") {
config.address[process.env.ENVIRONMENT] = deployedAddress;
await promises.writeFile("./config.json", JSON.stringify(config, null, 2));
}
console.log(`Deployment completed. Contract address: ${deployedAddress}`);
return deployedAddress;
};

const upgrade = async () => {
let address = config.address[process.env.ENVIRONMENT];
if (!address) {
console.log("Contract address not found. Please deploy first.");
return;
}
let contract = await getSmartContract();
let { pem, account } = await getPemAndAccount();
let code = await loadWasm();
let codeMetadata = createCodeMetadata(
config.deploymentArgs.payable,
config.deploymentArgs.payableBySc
);

const transaction = contract.upgrade({
caller: account.address,
code: code,
codeMetadata: codeMetadata,
initArguments: buildDeployArgs(),
gasLimit: config.deploymentArgs.gasLimit,
chainID: networkCfg.chain,
});
transaction.setNonce(account.getNonceThenIncrement());

console.log(`Upgrading contract on ${process.env.ENVIRONMENT}...`);
const txResult = await signAndSend(transaction, pem);
const deployedAddress = deploymentTransactionResultHandler(txResult);

if (deployedAddress !== "") {
config.address[process.env.ENVIRONMENT] = deployedAddress;
await promises.writeFile("./config.json", JSON.stringify(config, null, 2));
}
console.log(`Upgrade completed. Contract address: ${deployedAddress}`);
return deployedAddress;
};

const deploymentTransactionResultHandler = (transactionResult) => {
if (transactionResult.status.status !== "success") {
console.log("Transaction failed", transactionResult);
return "";
} else {
console.log(
"Deployment successful. Contract address: ",
transactionResult.logs.events[0].address.value
);
return transactionResult.logs.events[0].address.value;
}
};

const buildDeployArgs = () => {
const args = [];
config.deploymentArgs[process.env.ENVIRONMENT].forEach((arg) => {
switch (arg.type) {
case "TokenIdentifier":
args.push(new TokenIdentifierValue(arg.value));
break;
}
});
return args;
};

module.exports = {
loadNetworkConfig,
getPemAndAccount,
getSmartContract,
resultsParser: Parser,
signAndSend,
getProxyProvider,

deploy,
upgrade,
};
108 changes: 108 additions & 0 deletions scripts/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
{
"address": {
"DEVNET": "erd1qqqqqqqqqqqqqpgq7z49p3gkskpl0f8x45scaly22pqnww2z6ppsm54eug"
},
"deploymentArgs": {
"gasLimit": 85000000,
"payable": false,
"payableBySc": false,
"DEVNET": [
{
"name": "primary reward token id",
"type": "TokenIdentifier",
"value": "OURO-9ecd6a"
}
],
"MAINNET": [
{
"name": "primary reward token id",
"type": "TokenIdentifier",
"value": "OURO-9ecd6a"
}
]
},
"poolSettings": {
"DEVNET": {
"pools": [
{
"name": "Snake SFTs",
"collectionTokenIdentifier": "DEMIOU-704b5c",
"stakingModuleType": "SnakesSfts",
"scoreConfiguration": {
"All": {
"base": 0
},
"SnakesSfts": {
"base": 10000
}
}
},
{
"name": "Coding Division SFTs",
"collectionTokenIdentifier": "DHCD-bc9963",
"stakingModuleType": "CodingDivisionSfts",
"scoreConfiguration": {
"All": {
"base": 5,
"fullSetBonusScore": 25
},
"CodingDivisionSfts": {
"base": 5
}
}
},
{
"name": "Bloodshed NFTs",
"collectionTokenIdentifier": "BLOODSHED-a62781",
"stakingModuleType": "Bloodshed",
"scoreConfiguration": {
"All": {
"base": 1,
"granular": [
{
"nonceRange": {
"start": 555,
"end": 1157
},
"score": 3
},
{
"nonceRange": {
"start": 153,
"end": 554
},
"score": 4
},
{
"nonceRange": {
"start": 1,
"end": 152
},
"score": 11
}
]
}
}
},
{
"name": "XBunnies NFTs",
"collectionTokenIdentifier": "DHXBUNNIES-862129",
"stakingModuleType": "XBunnies",
"scoreConfiguration": {
"All": {
"base": 2,
"granular": [
{
"nonces": [388, 407, 25, 880, 274, 873, 1095, 175, 954, 1033],
"score": 160
}
]
}
}
}
],
"rewardTokens": []
},
"MAINNET": []
}
}
41 changes: 41 additions & 0 deletions scripts/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require("dotenv").config();
const config = require("./config.json");
const { loadNetworkConfig, deploy, upgrade } = require("./common");

const readline = require("readline");

const run = () => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});

rl.question(
"Please choose an option: \n 1. Deploy \n 2. Upgrade \n 3. Add Pool \n",
async (option) => {
switch (option) {
case "1":
await deploy();
rl.close();
break;
case "2":
await upgrade();
rl.close();
break;
case "3":
handleAddPools();
rl.close();
break;
default:
console.log("Invalid option");
}
}
);
};

const handleAddPools = async () => {
const pools = config.poolSettings[process.env.ENVIRONMENT].pools;
console.log(pools);
};

run();
Loading

0 comments on commit b67aeb2

Please sign in to comment.