Skip to content

Commit

Permalink
fix: auto-deploy testnet contracts with github actions
Browse files Browse the repository at this point in the history
  • Loading branch information
hstove committed Dec 29, 2020
1 parent ea54620 commit b1b5c97
Show file tree
Hide file tree
Showing 9 changed files with 312 additions and 24 deletions.
32 changes: 32 additions & 0 deletions .github/workflows/deploy-contracts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: deploy-contracts
on:
schedule:
- cron: "*/15 * * * *"
workflow_dispatch:

jobs:
deploy-contracts:
runs-on: ubuntu-latest
env:
API_SERVER: https://stacks-node-api.blockstack.org
CONTRACT_PRIVATE_KEY: ${{ secrets.CONTRACT_PRIVATE_KEY }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set Node Version
uses: actions/setup-node@v2-beta
with:
node-version: 12.16.1
- name: Restore lerna cache
id: lerna-cache
uses: actions/cache@v2
with:
path: |
node_modules
*/*/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
- name: Install monorepo deps
run: yarn --frozen-lockfile
if: steps.lerna-cache.outputs.cache-hit != 'true'
- name: Deploy contracts
run: yarn deploy-contracts
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"build:app": "yarn build:connect-ui && lerna run prod:web --scope @stacks/app",
"build:test-app": "lerna run prod:web --scope test-app",
"build:extension": "lerna run prod:ext",
"deploy-contracts": "lerna run deploy-contracts --stream",
"lint": "yarn lint:eslint && yarn lint:prettier",
"lint:eslint": "eslint \"packages/**/src/**/*.{ts,tsx}\" -f unix",
"lint:fix": "eslint \"packages/**/src/**/*.{ts,tsx}\" -f unix --fix",
Expand Down
1 change: 1 addition & 0 deletions packages/rpc-client/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import BN from 'bn.js';
import { serializeCV, ClarityValue } from '@blockstack/stacks-transactions';
import { TransactionResults } from '@blockstack/stacks-blockchain-sidecar-types';
import fetch from 'cross-fetch';

export interface Account {
balance: BN;
Expand Down
15 changes: 15 additions & 0 deletions packages/test-app/contracts/faker.clar
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
;; A component with a function that accepts all argument types.
;; Use for testing.

(define-public
(rawr
(arg-uint uint)
(arg-int int)
(arg-buff (buff 20))
(arg-string-ascii (string-ascii 20))
(arg-string-utf8 (string-utf8 20))
(arg-principal principal)
(arg-bool bool)
)
(ok u0)
)
19 changes: 6 additions & 13 deletions packages/test-app/contracts/status.clar
Original file line number Diff line number Diff line change
@@ -1,30 +1,23 @@
(define-map statuses
(
(author principal)
)
(
(status (buff 512))
)
)
(define-map statuses { author: principal } { status: (buff 128) })

(define-read-only (get-status (author principal))
(begin
(print author)
(default-to ""
(get status (map-get? statuses {author: author}))
(default-to 0x01
(get status (map-get? statuses { author: author }))
)
)
)

(define-public (write-status!
(status (buff 512))
(status (buff 128))
)
(begin
(print tx-sender)
(print status)
(map-set statuses
((author tx-sender))
((status status))
{ author: tx-sender }
{ status: status }
)
(ok status)
)
Expand Down
14 changes: 14 additions & 0 deletions packages/test-app/contracts/token.clar
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

(define-fungible-token connect-token)
(begin (ft-mint? connect-token u10000000 tx-sender))

(define-public (transfer
(recipient principal)
(amount uint)
)
(ok (ft-transfer? connect-token amount tx-sender recipient))
)

(define-public (faucet)
(ok (ft-mint? connect-token u100 tx-sender))
)
3 changes: 3 additions & 0 deletions packages/test-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"clean": "rm -rf ./dist",
"clean:all": "rm -rf ./dist && rm -rf ./coverage && rm -rf ./node_modules",
"dev": "cross-env NODE_ENV=development webpack-dev-server --hot --mode development",
"deploy-contracts": "ts-node scripts/deploy-contracts.ts -T --project ./tsconfig.json",
"lint": "yarn lint:eslint && yarn lint:prettier",
"lint:eslint": "eslint \"src/**/*.{ts,tsx}\" -f unix",
"lint:fix": "eslint \"src/**/*.{ts,tsx}\" -f unix --fix",
Expand Down Expand Up @@ -88,13 +89,15 @@
"html-webpack-plugin": "^4.2.0",
"jest": "^25.3.0",
"jest-puppeteer": "^4.3.0",
"nodemon": "^2.0.6",
"prettier": "^2.0.5",
"puppeteer": "^3.0.0",
"react-dev-utils": "^10.2.1",
"react-test-renderer": "^16.13.1",
"terser-webpack-plugin": "^2.3.5",
"ts-jest": "^25.3.1",
"ts-loader": "^7.0.0",
"ts-node": "^9.1.1",
"webpack": "^4.42.1",
"webpack-bundle-analyzer": "^3.7.0",
"webpack-chrome-extension-reloader": "^1.3.0",
Expand Down
110 changes: 110 additions & 0 deletions packages/test-app/scripts/deploy-contracts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { readFile as readFileFn } from 'fs';
import { promisify } from 'util';
import { RPCClient } from '@stacks/rpc-client';
import {
getAddressFromPrivateKey,
TransactionVersion,
makeContractDeploy,
StacksTestnet,
} from '@blockstack/stacks-transactions';
import * as BN from 'bn.js';
import * as dotenv from 'dotenv';
dotenv.config();

const readFile = promisify(readFileFn);

interface Contract {
name: string;
file?: string;
}

const contracts: Contract[] = [
{
name: 'connect-token',
file: 'token',
},
{
name: 'counter',
},
{
name: 'faker',
},
{
name: 'status-2',
file: 'status',
},
];

const rpcClient = new RPCClient(process.env.API_SERVER || 'http://localhost:3999');
const privateKey = process.env.CONTRACT_PRIVATE_KEY;
if (!privateKey) {
console.error('Provide a private key with `process.env.CONTRACT_PRIVATE_KEY`');
process.exit(1);
}
const address = getAddressFromPrivateKey(privateKey, TransactionVersion.Testnet);

const network = new StacksTestnet();
network.coreApiUrl = rpcClient.url;

const run = async () => {
const account = await rpcClient.fetchAccount(address);
console.log(`Account balance: ${account.balance.toString()} mSTX`);
console.log(`Account nonce: ${account.nonce}`);

const txResults: string[] = [];
let index = 0;
for (const contract of contracts) {
let exists: boolean;
const contractId = `${address}.${contract.name}`;
try {
await rpcClient.fetchContractInterface({
contractAddress: address,
contractName: contract.name,
});
exists = true;
} catch (error) {
// console.error(error);
exists = false;
}
if (exists) {
console.log(`Contract ${contractId} already exists.`);
continue;
}

console.log(`Deploying ${contractId}`);

const source = await readFile(`./contracts/${contract.file || contract.name}.clar`);
const tx = await makeContractDeploy({
contractName: contract.name,
codeBody: source.toString('utf8'),
senderKey: privateKey,
nonce: new BN(account.nonce + index, 10),
network,
});

const result = await rpcClient.broadcastTX(tx.serialize());
if (result.ok) {
index += 1;
txResults.push((await result.text()).replace(/"/g, ''));
} else {
const errorMsg = await result.text();
throw new Error(errorMsg);
}
}

if (txResults.length > 0) console.log('Broadcasted transactions:');
txResults.forEach(txId => {
console.log(`${rpcClient.url}/extended/v1/tx/0x${txId}`);
});
};

run()
.then(() => {
console.log('Finished successfully.');
process.exit();
})
.catch(error => {
console.error('Error while running:');
console.error(error);
process.exit(1);
});
Loading

0 comments on commit b1b5c97

Please sign in to comment.