介绍
现在,部署合同都是有趣的,而且游戏很快,除非您可以与它们互动:)
有两种方式可以通过两种方式与流有关的合同和帐户进行交互 - 您可以read
data(通过scripts
),您可以write
data(通过交易)。
最大的区别是 - 执行脚本时,您无法突变链的状态。即使您调用了合同的方法,该方法将修改状态,当脚本返回时,它也不会保留。
让我们从非侵入性交互开始 - 脚本。
先决条件
让我们假设我们正在使用上一部分的相同设置,并且我们已经有了cadence
文件夹。后来我们将一些脚本播放到scripts
文件夹中。
创建一个新的测试西装文件
npx @onflow/flow-js-testing make interaction
计算器
由于Cadence能够进行基本的数学操作,因此让我们尝试总和两个数字。导入executeScript
并在describe
块中添加新测试:
test("calculator", async () => {
const [result] = await executeScript({
code: `
pub fun main(a: Int, b: Int): Int{
return a + b
}
`,
args: ["10", "32"],
});
expect(result).toBe("42");
});
请注意,我们正在将数字作为字符串作为字符串,并且脚本返回的最终结果也将是字符串。您可以在闲暇时添加必要的转换,但这就是网络和库在流程上的处理编号
executeScript
将返回您一个元组(一个具有2个值的数组)[result, error]
,您可以
在您的断言中使用。
帐户管理
该框架为您提供了一种干净,简便的操作帐户方式-getAccountAddress
函数。
此功能将返回一个地址,您可以将其传递到任何类型的交互中,框架将为您完成其余的工作。例如,您可以将其用作交易签名或其中一个
函数中的参数:
const Alice = await getAccountAddress("Alice");
此方法很酷的是,该特定帐户可以在您的测试中的任何地方使用。因此,您可以创建一个辅助功能,再获取Alice的帐户地址,然后将其
可以解决完全相同的值! -
阅读平衡
计算器很有趣,而且游戏,但让我们做一些更实用的事情。例如,我们可以阅读任何帐户的流量平衡。如果您知道自己的节奏:
很容易
test("read balance", async () => {
const Alice = await getAccountAddress("Alice");
const [balance] = await executeScript({
code: `
pub fun main(address: Address): UFix64 {
let account = getAccount(address)
return account.balance
}
`,
args: [Alice],
});
expect(balance).toBe("0.00100000");
});
所有新创建的帐户以
0.001
的流量开始,因此我们可以断言此值并将其用作将来计算的基线
合同访问
从进口合同中阅读一些东西?很容易!让我们重新部署我们的Message
合同,并尝试从中读取message
字段:
test("read message", async () => {
const message = "noice";
await shallResolve(deployContractByName({ name: "Message", args: [message] }));
const [contractValue] = await executeScript({
code: `
import Message from 0x1
pub fun main():String{
return Message.message
}
`,
});
expect(contractValue).toBe(message);
});
我们可以从任何地址导入合同 - 框架将在将脚本发送给模拟器之前解决并分配适当的合同。
简短符号
让我们通过将Cadence代码移至read-message.cdc
文件并将其放入scripts
文件夹中来缩短该脚本符号。 executeScript
有一个速记概念,其中第一个参数是包含脚本的文件的名称。
test("read message - short", async () => {
const message = "noice";
await shallResolve(deployContractByName({ name: "Message", args: [message] }));
const [contractValue] = await executeScript("read-message");
expect(contractValue).toBe(message);
});
参数可以作为第二个参数传递。让我们添加来自上方代码的read-balance.cdc
文件,然后重写我们的余额阅读脚本:
test("balance - short", async () => {
const Alice = await getAccountAddress("Alice");
const [balance] = await executeScript("read-balance", [Alice]);
expect(balance).toBe("0.00100000");
});
noice!
Cadence忍者突变体ð¢
让我们加入一些突变!我们将创建两个帐户,其中一个帐户造成一些令牌,然后将其转移到另一个帐户。这应该很有趣!
为了铸造流,我们将利用mintFlow
函数,该功能期望地址和数量作为参数。导入文件顶部的mintFlow
和sendTransaction
test("FLOW transfer", async () => {
const Alice = await getAccountAddress("Alice");
const Bob = await getAccountAddress("Bob");
await mintFlow(Alice, "42");
const [aliceBalance] = await executeScript("read-balance", [Alice]);
expect(aliceBalance).toBe("42.00100000");
});
类似于我们如何使用shallPass
部署合同的方式,我们可以在发送交易时使用它。
test("FLOW transfer", async () => {
const Alice = await getAccountAddress("Alice");
const Bob = await getAccountAddress("Bob");
await mintFlow(Alice, "42");
const [aliceBalance] = await executeScript("read-balance", [Alice]);
expect(aliceBalance).toBe("42.00100000");
await shallPass(
sendTransaction({
code: `
import FungibleToken from 0x1
transaction(receiverAddress: Address, amount: UFix64){
prepare(sender: AuthAccount){
let receiver = getAccount(receiverAddress)
// Withdraw necessary amount into separate vault
let senderVault <- sender
.borrow<&{FungibleToken.Provider}>(from: /storage/flowTokenVault)!
.withdraw(amount: amount)
// Send to receiver
getAccount(receiverAddress)
.getCapability(/public/flowTokenReceiver)!
.borrow<&{FungibleToken.Receiver}>()!
.deposit(from: <- senderVault)
}
}
`,
args: [Bob, "1"],
signers: [Alice],
})
);
// Let's read updated balances and compare to expected values
const [newAliceBalance] = await executeScript("read-balance", [Alice]);
const [bobBalance] = await executeScript("read-balance", [Bob]);
expect(newAliceBalance).toBe("41.00100000");
expect(bobBalance).toBe("1.00100000");
});
通过简短符号发送
类似于我们如何使用脚本完成的方式,sendTransaction
功能还支持简短的符号:
sendTransaction(
fileName, // file name in "transactions folder"
[signers], // {optional) list of signers, even if that's single one
[arguments] // (optional) list of arguments
);
让我们从上面的示例中将所有Cadence代码移动到send-flow.cdc
文件夹下的send-flow.cdc
文件中,然后重写我们的测试:
test("FLOW transfer - short", async () => {
const Alice = await getAccountAddress("Alice");
const Bob = await getAccountAddress("Bob");
await mintFlow(Alice, "42");
const [aliceBalance] = await executeScript("read-balance", [Alice]);
expect(aliceBalance).toBe("42.00100000");
const signers = [Alice];
const recipient = Bob;
const amount = "1";
const args = [recipient, amount];
await shallPass(sendTransaction("send-flow", signers, args));
// Let's read updated balances and compare to expected values
const [newAliceBalance] = await executeScript("read-balance", [Alice]);
const [bobBalance] = await executeScript("read-balance", [Bob]);
expect(newAliceBalance).toBe("41.00100000");
expect(bobBalance).toBe("1.00100000");
});
“ Multisig”是从框外支持的,只需指定交易代码中的预期签名数,然后通过signers
参数传递相应的地址ð
仅此而已! Next time,我们将研究如何检查帐户存储并确保正确铸造或转移资源。直到下一次! ð