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

feat: add stellar upgrader contract to the deploy-contract.js script #487

Merged
merged 11 commits into from
Jan 9, 2025
59 changes: 53 additions & 6 deletions stellar/deploy-contract.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
'use strict';

const { Address, nativeToScVal, scValToNative, Operation, StrKey } = require('@stellar/stellar-sdk');
const { Address, nativeToScVal, scValToNative, Operation, StrKey, xdr, authorizeInvocation, rpc } = require('@stellar/stellar-sdk');
const { Command, Option } = require('commander');
const { loadConfig, printInfo, saveConfig } = require('../evm/utils');
const { getWallet, broadcast, serializeValue, addBaseOptions } = require('./utils');
const { getWallet, broadcast, serializeValue, addBaseOptions, getNetworkPassphrase } = require('./utils');
const { getDomainSeparator, getChainConfig } = require('../common');
const { prompt, validateParameters } = require('../common/utils');
const { weightedSignersToScVal } = require('./type-utils');
Expand Down Expand Up @@ -74,6 +74,10 @@ async function getInitializeArgs(config, chain, contractName, wallet, options) {
return { owner, gasCollector };
}

case 'upgrader': {
return {};
}

case 'example': {
const gatewayAddress = nativeToScVal(Address.fromString(chain?.contracts?.axelar_gateway?.address), { type: 'address' });
const gasServiceAddress = nativeToScVal(Address.fromString(chain?.contracts?.axelar_gas_service?.address), { type: 'address' });
Expand Down Expand Up @@ -139,30 +143,71 @@ async function uploadWasm(filePath, wallet, chain) {

async function upgrade(options, _, chain, contractName) {
const { wasmPath, yes } = options;
const contractAddress = chain.contracts[contractName].address;
const uncheckedContractAddress = chain.contracts[contractName].address;
const wallet = await getWallet(chain, options);

if (prompt(`Proceed with upgrade on ${chain.name}?`, yes)) {
return;
}

validateParameters({
isNonEmptyString: { contractAddress },
isNonEmptyString: { uncheckedContractAddress },
});

const contractAddress = Address.fromString(uncheckedContractAddress);

const newWasmHash = await uploadWasm(wasmPath, wallet, chain);
printInfo('New Wasm hash', serializeValue(newWasmHash));

const operation = Operation.invokeContractFunction({
contract: contractAddress,
contract: chain.contracts.upgrader.address,
cgorenflo marked this conversation as resolved.
Show resolved Hide resolved
function: 'upgrade',
args: [nativeToScVal(newWasmHash)],
args: [
nativeToScVal(contractAddress),
nativeToScVal(options.newVersion),
nativeToScVal(newWasmHash),
nativeToScVal([options.migrationData]),
],
cgorenflo marked this conversation as resolved.
Show resolved Hide resolved
auth: await createUpgradeAuths(contractAddress, newWasmHash, options.migrationData, chain, wallet),
});

await broadcast(operation, wallet, chain, 'Upgraded contract', options);
chain.contracts[contractName].wasmHash = serializeValue(newWasmHash);
printInfo('Contract upgraded successfully!', contractAddress);
}

async function createUpgradeAuths(contractAddress, newWasmHash, migrationData, chain, wallet) {
// 20 seems a reasonable number of ledgers to allow for the upgrade to take effect
cgorenflo marked this conversation as resolved.
Show resolved Hide resolved
const validUntil = await new rpc.Server(chain.rpc).getLatestLedger().then((info) => info.sequence + 20);

return Promise.all(
[
createAuthorizedFunc(contractAddress, 'upgrade', [nativeToScVal(newWasmHash)]),
createAuthorizedFunc(contractAddress, 'migrate', [nativeToScVal(migrationData)]),
].map((auth) =>
authorizeInvocation(
wallet,
validUntil,
new xdr.SorobanAuthorizedInvocation({
function: auth,
subInvocations: [],
}),
wallet.publicKey(),
getNetworkPassphrase(chain.networkType),
),
),
);
}

const createAuthorizedFunc = (contractAddress, functionName, args) =>
cgorenflo marked this conversation as resolved.
Show resolved Hide resolved
xdr.SorobanAuthorizedFunction.sorobanAuthorizedFunctionTypeContractFn(
new xdr.InvokeContractArgs({
contractAddress: contractAddress.toScAddress(),
functionName,
args,
}),
);

async function mainProcessor(options, processor, contractName) {
const config = loadConfig(options.env);
const chain = getChainConfig(config, options.chainName);
Expand Down Expand Up @@ -200,6 +245,8 @@ function main() {
.description('Upgrade a Stellar contract')
.argument('<contract-name>', 'contract name to deploy')
.addOption(new Option('--wasm-path <wasmPath>', 'path to the WASM file'))
.addOption(new Option('--new-version <newVersion>', 'new version of the contract'))
.addOption(new Option('--migration-data <migrationData>', 'migration data'))
cgorenflo marked this conversation as resolved.
Show resolved Hide resolved
.action((contractName, options) => {
mainProcessor(options, upgrade, contractName);
});
Expand Down
Loading