-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
1,665 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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": [] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); |
Oops, something went wrong.