generated from stacks-network/.github
-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[sBTC DR] Add test for asset contract (#97)
* feat: add test * fix: use mint instead of mint! * feat: improve print event, add more tests * chore: add clarinet to CI * fix: remove into_iter * chore: add missing shell scripts * chore: move bin folder to asset-contract * chore: remove sip 10 dependency * fix: remove extra space
- Loading branch information
Showing
20 changed files
with
667 additions
and
49 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
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 |
---|---|---|
|
@@ -2,3 +2,5 @@ target | |
Cargo.lock | ||
coverage | ||
romeo/testing/ | ||
romeo/asset-contract/.test | ||
romeo/asset-contract/.coverage |
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
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
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,32 @@ | ||
# Clarity contracts for sBTC DR 0.1 | ||
|
||
This folder contains clarity contracts and tools for clarity supporting the sBTC DR 0.1 (Romeo). | ||
|
||
## Contract `asset.clar` | ||
sBTC is a wrapped BTC asset on Stacks. | ||
|
||
It is a fungible token (SIP-10) that is backed 1:1 by BTC | ||
For this version the wallet is controlled by a centralized entity. | ||
sBTC is minted when BTC is deposited into the wallet and | ||
burned when BTC is withdrawn from the wallet. | ||
|
||
Requests for minting and burning are made by the contract owner. | ||
|
||
## Getting started for developers | ||
See https://stacks-network.github.io/sbtc-docs/ | ||
|
||
## Contributing | ||
|
||
### Unit tests | ||
Tests are written in clarity. It requires `clarinet`. You can install the lastest version via `./scripts/install_clarinet_*`. | ||
|
||
Run the unit tests through the script | ||
``` | ||
./scripts/tests.sh | ||
``` | ||
|
||
### Dependencies | ||
When updating the version of clarinet deno library for tests make sure to update | ||
* deps.ts | ||
* generate-test-ts | ||
* install_clarinet_action.sh |
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
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,6 @@ | ||
--- | ||
id: 0 | ||
name: Devnet deployment | ||
network: devnet | ||
stacks-node: "http://localhost:20443" | ||
bitcoin-node: "http://devnet:devnet@localhost:18443" |
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,13 @@ | ||
export function getContractName(contractId: string) { | ||
return contractId.split('.')[1]; | ||
} | ||
|
||
export function isTestContract(contractName: string) { | ||
return contractName.substring(contractName.length - 5) === "_test"; | ||
} | ||
|
||
/*eslint @typescript-eslint/no-explicit-any: ["error", { "ignoreRestArgs": true }]*/ | ||
export function exitWithError(...args: any[]) { | ||
console.error(...args); | ||
Deno.exit(1); | ||
} |
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,121 @@ | ||
import { Clarinet, Contract, Account } from 'https://deno.land/x/[email protected]/index.ts'; | ||
import { extractTestAnnotations, getContractName } from './utils/clarity-parser.ts'; | ||
import { defaultDeps, generateBootstrapFile, warningText } from './utils/generate.ts'; | ||
|
||
const sourcebootstrapFile = './tests/bootstrap.ts'; | ||
const targetFolder = '.test'; | ||
|
||
function isTestContract(contractName: string) { | ||
return contractName.substring(contractName.length - 5) === "_test" && | ||
contractName.substring(contractName.length - 10) !== "_flow_test"; | ||
} | ||
|
||
Clarinet.run({ | ||
async fn(accounts: Map<string, Account>, contracts: Map<string, Contract>) { | ||
Deno.writeTextFile(`${targetFolder}/deps.ts`, defaultDeps); | ||
Deno.writeTextFile(`${targetFolder}/bootstrap.ts`, await generateBootstrapFile(sourcebootstrapFile)); | ||
|
||
for (const [contractId, contract] of contracts) { | ||
console.log(contractId); | ||
const contractName = getContractName(contractId); | ||
if (!isTestContract(contractName)) | ||
continue; | ||
|
||
const hasDefaultPrepareFunction = contract.contract_interface.functions.reduce( | ||
(a, v) => a || (v.name === 'prepare' && v.access === 'public' && v.args.length === 0), | ||
false); | ||
const annotations = extractTestAnnotations(contract.source); | ||
|
||
const code: string[][] = []; | ||
code.push([ | ||
warningText, | ||
``, | ||
`import { Clarinet, Tx, Chain, Account, types, assertEquals, printEvents } from './deps.ts';`, | ||
`import { bootstrap } from './bootstrap.ts';`, | ||
`` | ||
]); | ||
|
||
for (const { name, access, args } of contract.contract_interface.functions.reverse()) { | ||
if (access !== 'public' || name.substring(0, 5) !== 'test-') | ||
continue; | ||
if (args.length > 0) | ||
throw new Error(`Test functions cannot take arguments. (Offending function: ${name})`); | ||
const functionAnnotations = annotations[name] || {}; | ||
if (hasDefaultPrepareFunction && !functionAnnotations.prepare) | ||
functionAnnotations.prepare = 'prepare'; | ||
if (functionAnnotations['no-prepare']) | ||
delete functionAnnotations.prepare; | ||
code.push([generateTest(contractId, name, functionAnnotations)]); | ||
} | ||
|
||
Deno.writeTextFile(`${targetFolder}/${contractName}.ts`, code.flat().join("\n")); | ||
} | ||
} | ||
}); | ||
|
||
type FunctionAnnotations = { [key: string]: string | boolean }; | ||
|
||
// generates contract call ts code for prepare function in mineBlock | ||
function generatePrepareTx(contractPrincipal: string, annotations: FunctionAnnotations) { | ||
return `Tx.contractCall('${contractPrincipal}', '${annotations['prepare']}', [], deployer.address)`; | ||
} | ||
|
||
/** | ||
* generates a mineBlock ts code containing optional prepare function | ||
* and the test function call | ||
*/ | ||
function generateNormalMineBlock(contractPrincipal: string, testFunction: string, annotations: FunctionAnnotations) { | ||
return `let block = chain.mineBlock([ | ||
${annotations['prepare'] ? `${generatePrepareTx(contractPrincipal, annotations)},` : ''} | ||
Tx.contractCall('${contractPrincipal}', '${testFunction}', [], callerAddress) | ||
]);`; | ||
} | ||
|
||
/** | ||
* Generates a mineBlock ts code containing | ||
* - optional block with prepare function, | ||
* - several empty blocks and | ||
* - the test function call | ||
* | ||
* supports the `@print events` annotations | ||
*/ | ||
function generateSpecialMineBlock(mineBlocksBefore: number, contractPrincipal: string, testFunction: string, annotations: FunctionAnnotations) { | ||
let code = ``; | ||
if (annotations['prepare']) { | ||
code = `let prepareBlock = chain.mineBlock([${generatePrepareTx(contractPrincipal, annotations)}]); | ||
prepareBlock.receipts.map(({result}) => result.expectOk()); | ||
`; | ||
if (annotations['print'] === 'events') | ||
code += `\n\t\tprintEvents(prepareBlock);\n`; | ||
} | ||
if (mineBlocksBefore > 1) | ||
code += ` | ||
chain.mineEmptyBlock(${mineBlocksBefore - 1});`; | ||
return `${code} | ||
let block = chain.mineBlock([Tx.contractCall('${contractPrincipal}', '${testFunction}', [], callerAddress)]); | ||
${annotations['print'] === 'events' ? 'printEvents(block);' : ''}`; | ||
} | ||
|
||
/** | ||
* Generates the ts code for a unit test | ||
* @param contractPrincipal | ||
* @param testFunction | ||
* @param annotations | ||
* @returns | ||
*/ | ||
function generateTest(contractPrincipal: string, testFunction: string, annotations: FunctionAnnotations) { | ||
const mineBlocksBefore = parseInt(annotations['mine-blocks-before'] as string) || 0; | ||
return `Clarinet.test({ | ||
name: "${annotations.name ? testFunction + ': ' + (annotations.name as string).replace(/"/g, '\\"') : testFunction}", | ||
async fn(chain: Chain, accounts: Map<string, Account>) { | ||
const deployer = accounts.get("deployer")!; | ||
bootstrap && bootstrap(chain, deployer); | ||
const callerAddress = ${annotations.caller ? (annotations.caller[0] === "'" ? `"${(annotations.caller as string).substring(1)}"` : `accounts.get('${annotations.caller}')!.address`) : `accounts.get('deployer')!.address`}; | ||
${mineBlocksBefore >= 1 | ||
? generateSpecialMineBlock(mineBlocksBefore, contractPrincipal, testFunction, annotations) | ||
: generateNormalMineBlock(contractPrincipal, testFunction, annotations)} | ||
block.receipts.map(({result}) => result.expectOk()); | ||
} | ||
}); | ||
`; | ||
} |
Oops, something went wrong.