Skip to content

Commit

Permalink
Added install.ts script to install contracts using Node
Browse files Browse the repository at this point in the history
  • Loading branch information
stellarsaur committed Feb 6, 2024
1 parent 697b2c2 commit 7722090
Show file tree
Hide file tree
Showing 7 changed files with 453 additions and 142 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ RUN if echo "$JS_STELLAR_SDK_NPM_VERSION" | grep -q '.*file:.*'; then \

ADD *.ts /home/tester/bin/
RUN ["sudo", "chmod", "+x", "/home/tester/bin/invoke.ts"]
RUN ["sudo", "chmod", "+x", "/home/tester/bin/install.ts"]

FROM base as build

Expand Down
2 changes: 1 addition & 1 deletion features/dapp_develop/dapp_develop.feature
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Feature: DApp Contract Development
Scenario Outline: DApp developer compiles, installs, deploys and invokes a contract
Given I used cargo to compile example contract <ContractExampleSubPath>
And I used rpc to verify my account is on the network
And I used cli to install contract <ContractExampleSubPath> / <ContractCompiledFileName> on network using my secret key
And I used cli to install contract <ContractExampleSubPath> / <ContractCompiledFileName> on network from tool <Tool> using my secret key
And I used cli to deploy contract <ContractExampleSubPath> / <ContractCompiledFileName> by installed hash using my secret key
When I invoke function <FunctionName> on <ContractName> with request parameters <FunctionParams> from tool <Tool> using my secret key
Then The result should be <Result>
Expand Down
6 changes: 3 additions & 3 deletions features/dapp_develop/dapp_develop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,13 @@ func deployContractUsingConfigParamsStep(ctx context.Context, contractExamplesSu
return nil
}

func installContractStep(ctx context.Context, contractExamplesSubPath string, compiledContractFileName string) error {
func installContractStep(ctx context.Context, contractExamplesSubPath string, compiledContractFileName string, tool string) error {

testConfig := ctx.Value(e2e.TestConfigContextKey).(*testConfig)
contractWorkingDirectory := fmt.Sprintf("%s/soroban_examples", testConfig.TestWorkingDir)

var err error
if testConfig.InstalledContractId, err = installContract(compiledContractFileName, contractWorkingDirectory, contractExamplesSubPath, testConfig.E2EConfig); err != nil {
if testConfig.InstalledContractId, err = installContract(compiledContractFileName, contractWorkingDirectory, contractExamplesSubPath, tool, testConfig.E2EConfig); err != nil {
return err
}

Expand Down Expand Up @@ -335,7 +335,7 @@ func initializeScenario(scenarioCtx *godog.ScenarioContext) {
scenarioCtx.Step(`^I used cli to add Network Config ([\S|\s]+) for rpc and standalone$`, createNetworkConfigStep)
scenarioCtx.Step(`^I used cli to add Identity ([\S|\s]+) for my secret key$`, createMyIdentityStep)
scenarioCtx.Step(`^I used cli to deploy contract ([\S|\s]+) / ([\S|\s]+) using Identity ([\S|\s]+) and Network Config ([\S|\s]+)$`, deployContractUsingConfigParamsStep)
scenarioCtx.Step(`^I used cli to install contract ([\S|\s]+) / ([\S|\s]+) on network using my secret key$`, installContractStep)
scenarioCtx.Step(`^I used cli to install contract ([\S|\s]+) / ([\S|\s]+) on network from tool ([\S|\s]+) using my secret key$`, installContractStep)
scenarioCtx.Step(`^I used cli to deploy contract ([\S|\s]+) / ([\S|\s]+) by installed hash using my secret key$`, deployContractStep)
scenarioCtx.Step(`^I used cli to deploy contract ([\S|\s]+) / ([\S|\s]+) using my secret key$`, deployContractStep)
scenarioCtx.Step(`^I used cli to add Identity ([\S|\s]+) for tester secret key$`, createTestAccountIdentityStep)
Expand Down
46 changes: 45 additions & 1 deletion features/dapp_develop/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dapp_develop

import (
"fmt"
"strings"

"github.com/go-cmd/cmd"

Expand Down Expand Up @@ -95,7 +96,50 @@ func deployContractUsingConfigParams(compiledContractFileName string, contractWo
}

// returns the installed contract id
func installContract(compiledContractFileName string, contractWorkingDirectory string, contractExamplesSubPath string, e2eConfig *e2e.E2EConfig) (string, error) {
func installContract(compiledContractFileName string, contractWorkingDirectory string, contractExamplesSubPath string, tool string, e2eConfig *e2e.E2EConfig) (string, error) {
var response string
var err error

switch tool {
case "CLI":
response, err = installContractFromCliTool(compiledContractFileName, contractWorkingDirectory, contractExamplesSubPath, e2eConfig)
case "NODEJS":
response, err = installContractFromNodeJSTool(compiledContractFileName, contractWorkingDirectory, contractExamplesSubPath, e2eConfig)
default:
err = fmt.Errorf("%s tool not supported for invoke yet", tool)
}

if err != nil {
return "", err
}

return response, nil
}

func installContractFromNodeJSTool(compiledContractFileName string, contractWorkingDirectory string, contractExamplesSubPath string, e2eConfig *e2e.E2EConfig) (string, error) {
args := []string{

"--wasm", fmt.Sprintf("./%s/%s/target/wasm32-unknown-unknown/release/%s", contractWorkingDirectory, contractExamplesSubPath, compiledContractFileName),
"--rpc-url", e2eConfig.TargetNetworkRPCURL,
"--source", e2eConfig.TargetNetworkSecretKey,
"--network-passphrase", e2eConfig.TargetNetworkPassPhrase,
}
envCmd := cmd.NewCmd("./install.ts", args...)
status, stdOutLines, err := e2e.RunCommand(envCmd, e2eConfig)
stdOut := strings.TrimSpace(strings.Join(stdOutLines, "\n"))

if status != 0 || err != nil {
return "", fmt.Errorf("nodejs install of example contract %s had error %v, %v, stdout: %v", compiledContractFileName, status, err, stdOut)
}

if stdOut == "" {
return "", fmt.Errorf("nodejs install of example contract %s did not print any response", compiledContractFileName)
}

return stdOut, nil
}

func installContractFromCliTool(compiledContractFileName string, contractWorkingDirectory string, contractExamplesSubPath string, e2eConfig *e2e.E2EConfig) (string, error) {
envCmd := cmd.NewCmd("soroban",
"contract",
"install",
Expand Down
91 changes: 91 additions & 0 deletions install.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#!/usr/bin/env ts-node-script

import * as fs from 'fs';
import { ArgumentParser } from 'argparse';
import {
Keypair,
TransactionBuilder,
SorobanRpc,
scValToNative,
xdr,
Operation,
OperationOptions,
} from '@stellar/stellar-sdk';

const { Server } = SorobanRpc;

async function main() {
const parser = new ArgumentParser({ description: 'Install a contract' })

parser.add_argument('--wasm', { dest: 'wasm', required: true, help: 'Path to wasm binary' });
parser.add_argument('--rpc-url', { dest: 'rpcUrl', required: true, help: 'RPC URL' });
parser.add_argument('--source', { dest: 'source', required: true, help: 'Secret key' });
parser.add_argument('--network-passphrase', { dest: 'networkPassphrase', required: true, help: 'Network passphrase' });

const {
wasm,
rpcUrl,
networkPassphrase,
source,
} = parser.parse_args() as Record<string, string>;


const server = new Server(rpcUrl, { allowHttp: true });
const secretKey = Keypair.fromSecret(source);
const account = secretKey.publicKey();
const sourceAccount = await server.getAccount(account);
const wasmBuffer = fs.readFileSync(wasm);


const options: OperationOptions.InvokeHostFunction = {
"func": xdr.HostFunction.hostFunctionTypeUploadContractWasm(Buffer.from(wasmBuffer)),
"source": account
};
const op = Operation.invokeHostFunction(options);

const originalTxn = new TransactionBuilder(sourceAccount, {
fee: "100",
networkPassphrase
})
.addOperation(op)
.setTimeout(30)
.build();

const txn = await server.prepareTransaction(originalTxn);
txn.sign(secretKey);
const send = await server.sendTransaction(txn);
if (send.errorResult) {
throw new Error(`Transaction failed: ${JSON.stringify(send)}`);
}
let response = await server.getTransaction(send.hash);
for (let i = 0; i < 50; i++) {
switch (response.status) {
case "NOT_FOUND": {
// retry
await new Promise(resolve => setTimeout(resolve, 100));
response = await server.getTransaction(send.hash);
break;
}
case "SUCCESS": {
if (!response.returnValue) {
throw new Error(`No invoke host fn return value provided: ${JSON.stringify(response)}`);
}

const parsed = scValToNative(response.returnValue);
console.log(JSON.stringify(parsed));
return;
}
case "FAILED": {
throw new Error(`Transaction failed: ${JSON.stringify(response)}`);
}
default:
throw new Error(`Unknown transaction status: ${response.status}`);
}
}
throw new Error("Transaction timed out");
}

main().catch(err => {
console.error(JSON.stringify(err));
throw err;
});
Loading

0 comments on commit 7722090

Please sign in to comment.