BETA

Challenges > Web3 前端训练营招募|2024,成为 Web3 开发 > Web3前端进阶实战

Share

EVM++开发基础

1. Aspect

Aspect

Aspect 引入了一种动态机制,以在区块链平台上实现系统级功能。Aspect 基本上类似于一个系统扩展,以 WebAssembly (WASM) 形式执行,可以绑定到任何智能合约上。这种特性提供了可以增强目标合约的功能或监控其活动,在满足预定义条件时触发特定操作的灵活性。

什么是 Aspect?

Aspect 的强大之处在于其基于 WebAssembly (WASM)。在 Artela 上,我们开发了一个专门的 WASM 运行时,名为 Aspect-Runtime,专用于在平台上执行 Aspects。Aspect Runtime 通过一组预定义的 host APIs 实现 Aspect 与区块链核心模块之间的交互。这些 APIs 使得 Aspect 具备多种能力,如读取区块链状态、调用智能合约、管理自身状态等。

有网站开发背景的人对于“中间件(middleware)”这个概念可能会比较熟悉。对于其他开发者,以下是更详细的说明。

网站框架中的拦截器

拦截器是一段可以集成到 Web 服务器中以扩展其功能的代码。想象一下使用拦截器为网站服务器添加认证或日志记录功能,如下所示:

4.png

当接收到 HTTP 请求时,网站服务器的拦截器机制(在某些框架中称为中间件)允许开发人员创建模块来处理此传入请求,可以在主要处理之前或之后进行。例如,认证拦截器可以在请求到达核心逻辑之前验证用户凭据。这种设计模式使得认证和日志记录等功能模块化,确保设计的灵活性。

这种拦截器设置还支持共享上下文,使不同的中间件或路由器之间可以进行通信。例如,在认证用户之后,相关拦截器可以将用户数据存储在共享上下文中,后续的拦截器或路由处理器可以访问这些数据而无需重新加载。

Artela 中的 Aspect

相应地,Aspect 可以被视为智能合约的拦截器。想象一下在 Artela 上开发一个去中心化交易所并集成 Aspects:

5.png

正如拦截器可以增强网站框架,Aspect 也可以增强你的 dApps。它们支持模块化设计,并通过共享上下文促进模块间的通信。

Aspect 如何工作?

扩展层

在 Artela 中,我们提供了一个内置的扩展层,它位于应用层(包括智能合约)和基础层(区块链核心模块,如共识、内存池、P2P 网络)之间。这个扩展层允许开发人员为特定智能合约开发链级扩展,定制交易处理流程。

6.png

Aspect Core是一个系统模块,负责管理 Aspects 的生命周期,包括部署、执行、绑定、升级和销毁。它也作为一个系统合约部署在 0x0000000000000000000000000000000000A27E14,可以通过 EVM 合约调用。Aspect 核心的实现是用本地代码编写的,以减少 VM 启动的开销。

7.png

Aspect 生命周期

Aspect 的生命周期由 Aspect Core管理,包括以下阶段:

  • 编译:Aspect 由 Aspect 编译器编译成 WASM 字节码。
  • 部署:编译后的 WASM 字节码可以部署到区块链并初始化某些属性。
  • 绑定:为了在连接点触发,Aspect 必须绑定到智能合约。这个绑定过程是通过调用 Aspect 核心的 bind 方法完成的。
  • 执行:Aspect 的执行可以在预定义的连接点触发,也可以通过直接调用 Aspect 的 operation 接口触发。

有关 Aspect 生命周期的更多详情,请参阅 此处 的文档。

Aspect 能做什么?

插入‘即时’调用

即时调用(Just-in-Time Call)是一种可以在合约调用层连接点插入到当前 EVM 调用栈中的调用。有关即时调用概念的详细解释,请参阅 此处 的文档。

与其他 的Aspects 、智能合约共享信息

虽然 Aspect 和智能合约的执行环境是不同的(WASM 与 EVM),但它们并非完全隔离。可以通过 Aspect Context 在 Aspects 和智能合约之间共享信息。Aspect Context 本质上是一种临时存储,其生命周期仅限于当前交易。Aspect Context 可用于在 Aspect 和智能合约之间进行双向通信。

有关此主题的更多详情,请阅读 此处 的文档。

从 EVM 智能合约查询

Aspect 可以对 EVM 智能合约进行只读调用,获取最新状态(目前不允许查询历史状态)。在某些情况下,比如构建一个价格预言机 Aspect 时,可以利用此功能从预言机合约中查询最新价格,并将其保存到 Aspect Context,与智能合约共享。有关此主题的更多详情,请阅读 此处 的文档。

状态变更追踪

Aspects 可以追踪智能合约状态的变化。例如,Aspect 可以基于输入参数检查智能合约的提交状态是否如预期。

这种追踪由 ASOLC 编译器生成的额外操作码和中间表示方法来实现:

8.png

有关此主题的更多信息,请参阅 ASOLC 文档

像智能合约一样维护状态

类似于智能合约,Aspect 也是有状态的。每个 Aspect 维护其自身状态,这些状态将被持久化在全局状态中。目前 Aspect 仅提供一个简单的键值存储,更好的存储绑定将在后续版本中提供。要将数据持久化到区块链全局状态中,可以使用以下方法:

// read & write state
let state = sys.aspect.mutableState.get<string>('state-key');
let unwrapped = state.unwrap();
state.set('state-value');

处理调用

Aspect 也可以独立操作。它可以通过 bytes-in-bytes-out 的 operation 方法处理外部交易或调用:

class AspectTest implements IAspectOperation {
  operation(input: OperationInput): Uint8Array {
    // handle incoming request and echo it back
    return data;
  }
}

注意

请注意,operation 方法只是一个 bytes-in-bytes-out 的入口点,如果 Aspect 中有任何敏感数据,请确保正确进行身份验证。

更多信息

Artela Aspect 的初始版本是使用 AssemblyScript(TypeScript 的一个子集,具有严格的类型)构建的。为了方便 Aspect 的开发,我们还创建了 Aspect Tooling,这是一个用于与底层 host APIs 交互的库和工具集。有关更多详情,请查看我们的仓库。

2. Join Point

Join Point

每个Join Point表示特定交易生命周期阶段的状态转换函数。这些Join Point由不同的元数据(概述触发条件、可以访问的系统模块以及可以利用的运行时上下文)和一个入口函数(例如,你的 Aspect 类中的 PreTxExecute 方法)构成。

连接点分为以下几类:

  • 区块级Join Point:由区块链自动生成的新区块事件触发。
  • 交易级Join Point:由 EOA 交易触发。
  • 调用级Join Point:由合约交互触发。值得注意的是,跨合约交互也会触发这些连接点。

更直观的表示请参见下图:

9.png

入口函数

每个Join Point都有一个专用的入口函数接口,包括:

  • 唯一的函数名称
  • 相关输入参数
  • 预期输出

要使 Aspect 在Join Point内处于活动状态,它必须包含与给定入口函数对齐的函数,通过相应的入口函数启动 Aspect。

10.png

互操作接口

互操作接口在以下方面发挥作用:

  • 读取和写入运行时上下文
  • 访问基础层模块

通过这些接口,Aspects 可以利用核心区块链功能。一些连接点可能提供多个系统调用。这些接口使 Aspects 能够定制交易或区块处理流程。

image.png

布局

image.png

在 Aspect 编程中,在区块和交易的生命周期中定义了各种 join points 。此框架使 Aspect 开发者能够为 dApps 创建自定义增强功能。

每个 join point 的全面概述:

Join Point描述
FilterTx当 RPC 服务器接收到此交易时触发,请注意此 join point 在共识之外,因此不允许在此处修改 Aspect 状态。
OnTxVerify当交易签名正在验证时触发,Aspect 可以用自定义验证逻辑替换内置的 secp256k1 签名验证。
OnBlockInitialize在准备区块提案之前激活,允许在此时插入自动化交易。
PreTxExecute在交易执行之前触发。在此阶段,账户状态保持原始状态,允许 Aspect 根据需要预加载信息。
PreContractCall在跨合约调用执行之前触发。例如,在 TX 执行期间,Uniswap 合约调用 Maker 合约,Aspect 将被执行。
PostContractCall在跨合约调用执行之后触发。Aspect 可以检查合约的 post-call 状态,并做出后续执行决策。
PostTxExecute一旦交易执行完毕且账户状态已定型后激活。随后,Aspect 可以对最终执行状态进行全面审查。
OnTxCommitted在交易定型后触发,交易引起的修改状态已刷新到状态数据库。在此阶段,Aspect 可以进行后处理活动,例如启动可以在未来区块中执行的异步任务。
OnBlockFinalize在区块定型后触发。允许 Aspect 提交可以在未来区块中执行的异步任务。

值得注意的是,join points 提供了抽象定义,适应多种区块链实现、不同的 join point 暴露和其他平台特定属性。

上下文

运行时上下文为 Aspects 提供了关于交易和区块处理的关键见解,包括智能合约状态更新、记录的事件和交易原始数据。

上下文被设计为键值对集合,当 Aspect 使用特定键查询时,上下文提供最新的关联值。例如,使用 tx^content^nonce 查询将返回初始交易的 nonce 字段值。为了保持共识机制的对齐和一致性,区块链网络中的所有节点必须为区块中的特定交易键提供一致的值。

上下文子集

上下文包括四个主要类别:

  • 区块信息:不可变的区块详细信息,如其哈希或验证者签名。
  • 交易数据:静态交易相关数据,如输入数据、收据和交易执行期间的状态变化。
  • Aspect 可修改数据:Aspects 可以修改的数据,每个 Aspect 在其命名空间内工作,确保数据修改不会与其他 Aspects 重叠。
  • 环境数据:与节点相关的环境变量,包括网络配置。

上下文生命周期

上下文的生命周期仅限于区块处理期间。当一个区块开始处理时,区块中的每个交易都获得一个新的上下文,包含交易特定的详细信息,确保交易执行期间的一致性。

在区块处理结束时,上下文终止。需要理解的是,这种终止不会影响区块链状态或交易结果,两者都永久存储在区块链上。上下文主要保存临时数据,帮助交易过程并维持区块内的状态一致性。

3. Aspect Lifecycle & Binding

Aspect 生命周期

Aspect 的生命周期包括多个阶段:部署、升级、配置、绑定、解绑、执行和销毁。

需要注意的是,Aspect 核心是一个位于地址 0x0000000000000000000000000000000000A27E14 的系统合约,管理所有 Aspect 生命周期操作。有关 Aspect 核心 ABI 的更多详细信息,请参考 此链接

部署

部署一个 Aspect 类似于部署传统智能合约。通过 EOA 交易部署 Aspect 时,需要提供以下关键信息:

参数名称必要性描述
codeAspect 的 WASM 工件字节码,以十六进制格式表示。
propertiesAspect 的初始只读状态。
account结算账户,负责支付 Aspect 的 gas 费用。某些 Aspect 操作会产生 gas 成本。目前,结算账户默认是合约调用的发送者,但未来版本将支持自定义结算账户。
proof未来支持自定义结算账户绑定验证的占位符。

与智能合约一样,一旦部署,Aspect 将接收一个唯一的 ID,等同于 EVM 地址类型(20 字节)。初始部署时,Aspect 的版本为 1.

Aspect 的状态可以表示为 JSON 形式:


{
  "id": "0xABCDEF....",
  "code": {
    "1": "0xABCDEF...."
  },
  "properties": {
    "property-name": "property-value",
    ...
  },
  "settlementAccount": "0xABCDEF....",
  "currentVersion": 1
}

升级 Aspect 不会中断其当前的绑定状态。绑定到旧版 Aspect 的合约将继续执行旧代码。请确保你的 Aspect 具有向后兼容性,以防发生意外。

Binding

Binding将 Aspect 与特定智能合约关联起来。只有通过合约中定义的 isOwner(address): bool 方法验证的智能合约所有者才能发起此过程。

绑定过程需要:

参数名称必须描述
aspectId要绑定的 Aspect 的 ID。
aspectVersion要绑定的 Aspect 的版本。使用 0 绑定到最新版本。
account要与 Aspect 绑定的账户地址。
priorityAspect 的执行优先级。数字越小,优先级越高。对于优先级相同的 Aspect,先绑定的先执行。

Aspect 核心合约记录绑定关系如下:

{
  "0x{AccountAddress}": [
    {
      "aspectId": "0x{AspectId1}",
      "aspectVersion": 1
    },
    {
      "aspectId": "0x{AspectId2}",
      "aspectVersion": 2
    }
    ...
  ]
}

绑定

绑定是基于 Aspect 开发中的一个重要过程。只有当 Aspect 绑定到特定智能合约时,Aspect 才能在某些 join points 被触发。EoA 也可以与 Aspect 绑定,Aspect 可以为 EoA 提供自定义交易验证过程。

步骤

image.png

  1. 启动绑定:账户所有者通过使用其 Externally Owned Account (EOA) 签署绑定交易来启动绑定过程。
  2. 合约所有权验证(仅对合约账户需要):Aspect Core 系统合约调用智能合约的 isOwner(address) returns (bool) 方法以验证发送者的地址。如果验证失败(例如,由于未实现的验证方法或地址验证失败),绑定交易将被回滚。
  3. Aspect 允许检查:在成功的合约所有权验证后,调用给定 Aspect 的 onContractBinding(address) bool 方法。这检查当前智能合约是否可以与给定 Aspect 绑定。如果此验证失败,交易将被回滚。
  4. 完成绑定:如果所有检查都通过,Aspect Core 系统合约在全局状态中保存关系。随后,当调用智能合约时,Aspect 将在指定的 join points 被触发。

注意:只有在给定版本的 Aspect 和智能合约都已部署时,才能建立绑定关系。不能与不存在的 Aspect 或智能合约绑定。

合约所有权验证

为了支持与 Aspect 绑定,智能合约必须实现 isOwner(address) returns (bool) 方法。此方法对于验证智能合约的所有权至关重要。如果方法调用失败或返回 false,绑定交易将不会成功。在此方法中实现自定义逻辑可以进行更复杂的所有权验证,例如多签名验证。

合约地址验证

Aspects 可以拒绝来自某些合约的绑定。当智能合约尝试与 Aspect 绑定时,调用 onContractBinding(address) bool 钩子。如果此方法返回 false 或未实现,绑定交易将失败。要将 Aspect 限制为某些合约,可以在此方法中实现白名单检查。或者,为了公共可访问性,简单返回 true。

优先级

绑定请求中的优先级是一个无符号的 8 位整数。它决定了 Aspect 执行的顺序,优先级编号最低的 Aspect 最先执行。如果多个 Aspects 具有相同的优先级,则最早绑定的 Aspect 最先执行。

image.png

Aspect 数量限制

一个账户最多可以绑定 255 个 Aspects,但这取决于 Aspect 类型。特殊情况是交易验证器 Aspect,如果你为合约账户绑定这种 Aspect,每个账户只能绑定一个 Aspect。

Unbinding

Aspect 可以从从智能合约中分离。只有智能合约的所有者才能发起解绑,其地址必须通过 isOwner(address): bool 验证。

要解绑,则需要:

参数名称必须描述
aspectId要解绑的 Aspect 的 ID。
account要分离的账户地址。

一旦解绑,当接收到与给定账户相关的交易时,Aspect 将不会执行。

执行

Aspect 执行在以下两种情况下触发:

  1. 针对绑定的智能合约地址的 EOA 交易或调用。
  2. 直接调用 Aspect 的操作方法的 EOA 交易。

在Join Point处执行

在 Aspect 中,有一组预定义的方法会在特定连接点处触发。在交易处理的某个阶段,Aspect 的入口函数会被调用,并将路由到与当前连接点配对的相应方法。例如,PreContractCall 方法将在合约调用之前执行。

使用 EOA 交易执行

每个 Aspect 都有一个 bytes-in-bytes-outoperation 方法。此方法是维护接口,允许 Aspect 维护者通过交易 / 调用更新或获取 Aspect 状态。如果你的 Aspect 包含敏感数据,请确保在修改状态之前实现必要的授权检查。目前,operation 方法以 bytes in bytes out 格式工作,开发人员需要自行管理编码、解码和路由。未来版本将提供更简化的解决方案。

4. Aspect Programming

Aspect 编程

Aspect 编程引入了一种围绕连接点模型 (JPM) 构建的强大范式。该模型围绕三个关键组件展开:

  • JoinPoint:表示 Aspect 将在何处执行。它代表事务和区块处理流程中的特定时刻,作为引入附加功能的钩子。
  • Aspect:定义将在连接点执行的代码。Aspect 可以进入运行时上下文并执行系统调用,这使其能够积极参与事务的生命周期。
  • Binding:描述 Aspect 将在何时执行。智能合约创建者可以将 Aspect 绑定到其智能合约内的某些连接点。当事务处理与这些点对齐时,相关的 Aspect 将被激活。

image.png

Aspect 编程概述

为说明 Aspect 编程的工作原理,考虑一个包含大量存款的保险库智能合约。目标是在运行时保护智能合约,使其免受可能非法重定向存款的潜在威胁。一个 Aspect 示例被设计用于监督和验证智能合约执行后的保险库变更。如果检测到资金流动异常,此 Aspect 将取消可疑事务。此 Aspect 的执行在智能合约执行后、事务调用合约时启动。

Aspect 和Join Points

一个 Aspect 被设计为clas,是基础 Aspect 接口的扩展。它包含指示Join Points的方法,添加的逻辑可以在这些方法中交织。以下是一个示例:

class Aspect implements IPostTxExecuteJP {
/**
      * postTxExecute is a join-point invoked post the completion of transaction execution but prior to state commitment.
      *
      * @param ctx context of the designated join-point
      * @return outcome of the Aspect's execution
      */
    postTxExecute(input: PostTxExecuteInput): void {
        if (ethereum.parseMethodSig(ctx.tx.content.data) == ethereum.computeMethodSig('withdraw')) {
            const withdrawAmount = ...// extract withdraw amount from calldata via context.tx.content.data
            const vaultBalance = new VauleState(ctx, ctx.tx.content.to).get('vauleBalance').current();// state change wrapper class generated by aspect-tool
            if (vaultBalance != withdrawAmount) {
                sys.revert('Error: Balance discrepancy detected.');
            }
        }
    }
}

在上述示例中,postTxExecute 方法充当join point的入口,在 EVM 完成事务后发挥作用。

PostTxExecuteCtx 对象表示运行时上下文,提供了对原始事务及其在当前join point上的细节的洞察。它被传递给join point方法,促进其与正在进行的事务的交互。

示例 Aspect 在事务期间检查智能合约的 withdraw 函数的调用。如果被调用,事务参数中的预期提款金额将与保险库的实时资金流动进行交叉检查。发现差异(通常是编码错误或恶意攻击)时,Aspect 将激活 revert() 函数,通过运行时上下文的事务管理对象来撤销该事务。

部署与绑定

要将一个 Aspect 集成到区块链中,其字节码需要嵌入到一个部署事务中。这个事务与一个 Aspect 系统合约交互,将 Aspect 的字节码记录到区块链的全局状态中。这允许验证者访问字节码,使其在激活时能够执行 Aspect 的逻辑。

然而,一个 Aspect 只有在绑定到特定智能合约时才会启动。它需要智能合约所有者使用其外部拥有的账户 (EOA) 签署一个绑定事务 - 该账户应绕过智能合约的 isOwner(address) returns (bool) 方法的检查。这个事务包含智能合约地址和 Aspect ID,并在执行时涉及 Aspect 系统合约,将智能合约和 Aspect 在区块链的全局状态中绑定起来。这确保只有智能合约的合法所有者可以绑定 Aspect,防止任何未授权的绑定尝试。

在成功执行部署和绑定事务后,Aspect 被集成到区块链并绑定到智能合约中,增强了合约的功能并提高了其安全性。

Aspect 的执行

每当智能合约被事务调用时,Aspect 编程框架运行时将被激活。随着事务的开始,Aspect 运行时评估每个join point,识别与调用的智能合约相关联的 Aspect。如果发现匹配,Aspect 在事务执行后被触发。

要执行 Aspect,它的字节码从区块链的全局状态中获取,并引入到 WebAssembly (WASM) 运行环境中。随后,创建一个详细描述事务及其环境的上下文对象。这为 Aspect 运行时调用 Aspect 的主要功能设置了舞台,促使其逻辑执行。

例如,考虑一个智能合约中的 withdraw 函数,设计用于确保资金转账始终超过函数参数中规定的金额。如果一个 Aspect 绑定到 postTxExecute 的join point,一旦事务结束,它将被激活。Aspect 运行时检索字节码,在 Wasm 环境中启动它,创建一个上下文对象,并调用 Aspect 的主要功能。如果 Aspect 检测到资金转账中的任何差异,它可以通过join point上下文标记一个反转。Aspect 运行时在识别到此标志时,将撤销事务,将其标记为失败,并提供交易因关联 Aspect 被推翻的理由。

总结

Aspect 编程框架的核心是Join Point模型 (JPM)。该模型在事务和区块处理序列中提供战略插入点,为附加逻辑铺平了道路。Aspect 体现了这些附加逻辑,可以进入运行时上下文并进行系统调用,将其加入到交易的生命周期中。通过将 Aspect 绑定到指定的Join Points,智能合约所有者可以增强合约的功能并提高其安全性。

后续章节将深入探讨Join Point模型的复杂设计及其实现。

5. Develop an Aspect

开发一个 Aspect

本节将指导您在 Artela 上使用示例 Aspect 构建 dApp。此 Aspect 作为原生扩展,与智能合约协同处理,并且可以在交易生命周期的各个阶段注入。在本例中,我们将展示 Aspect 如何识别并回滚特定交易。

先决条件:

1. 创建一个新项目

确保您已安装最新版本的 Node.js 和 npm,首先安装 aspect-tool:

npm install -g @artela/aspect-tool

项目初始化:要使用 aspect-tool 启动项目,请按照以下步骤操作:

# 创建一个新目录并进入该目录
mkdir my-first-aspect && cd my-first-aspect

# 使用 aspect-tool 初始化 npm 项目
aspect-tool init

# 安装必要的依赖项
npm install

这将创建一个具有以下结构的项目目录:

.
├── README.md
├── asconfig.json
├── aspect               <-- Aspect代码放在这里
│   └── index.ts         <-- Aspect的入口函数
├── contracts            <-- 智能合约放在这里
├── package.json
├── project.config.json
├── scripts             <-- 实用脚本,包括部署、绑定等
│   ├── aspect-deploy.cjs
│   ├── bind.cjs
│   ├── contract-call.cjs
│   ├── contract-deploy.cjs
│   ├── contract-send.cjs
│   └── create-account.cjs
├── tests
└── tsconfig.json

2. 部署智能合约

2.1 添加智能合约

在项目主目录的 contracts 项中,创建扩展名为 .sol 的智能合约源文件。

例如,创建一个 HelloWorld.sol 文件:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.2 <0.9.0;

contract HelloWorld {
    address private owner;
    constructor() {
        owner = msg.sender;
    }
    function isOwner(address user) external view returns (bool result) {
        return user == owner;
    }

    // print hello message
    function hello() public pure returns (string memory) {
        return "hello";
    }

    // print world message
    function world() public pure returns (string memory) {
        return "world";
    }
}

2.2 编译智能合约

此步骤依赖于 solc,首先检查 solc 是否正确安装:

solc --version

使用以下命令编译您的合约:

npm run contract:build

✅ 成功编译后将在 build/contract 目录中生成 HelloWorld.abi 和 HelloWorld.bin 文件。

2.3 部署智能合约

2.3.1 更新 project.config.json

在根目录中的 project.config.json 中更新适当的网络配置:

{
  "node": "https://betanet-rpc1.artela.network"
}

💡 有关开发环境设置的更多详细信息,请参阅 artela devnet

2.3.2 创建区块链账户(可选)

在 my-first-aspect 文件夹下执行以下命令以创建账户(如果尚未创建):

npm run account:create

✅ 如果账户创建成功,其私钥将作为 privateKey.txt 转储在当前目录中。

💡 有关此命令的详细用法,请参阅 create-account 命令文档。

如果您的账户缺少测试代币,请加入 Discord,并在 testnet-faucet 频道领取一些。

2.3.4 部署合约

在 my-first-aspect 文件夹中执行以下命令,使用提供的脚本部署合约:

npm run contract:deploy --  --abi ./build/contract/HelloWorld.abi \
                           --bytecode ./build/contract/HelloWorld.bin

✅ 部署成功后,终端将显示合约地址。

💡 有关此命令的详细用法,请参阅 deploy-contract 命令文档。

2.4 调用合约

2.4.1 调用 hello 方法

在 my-first-aspect 文件夹中执行以下命令,调用合约:

npm run contract:call -- --contract {smart-contract-address}  \
                         --abi ./build/contract/HelloWorld.abi   \
                         --method hello

将占位符 {smart-contract-address} 替换为步骤 2.3 中部署智能合约时获得的信息。

✅ 成功后,终端将显示调用结果。

💡 有关此命令的详细用法,请参阅 contract-call 命令文档。

2.4.2 调用 world 方法

npm run contract:call -- --contract {smart-contract-address}  \
                         --abi ./build/contract/HelloWorld.abi   \
                         --method world

✅ 如果返回 world 字符串,则说明 HelloWorld 合约已经成功部署。

3. 创建你的 Aspect

3.1 实现一个 Aspect

Aspect 源文件可以在 aspect/index.ts 中找到。

例如,要在智能合约调用执行后添加逻辑,打开 index.ts,找到 postContractCall 函数,并插入您的逻辑:

typescript复制代码
postContractCall(input: PostContractCallInput): void {
    // Implement me...
}

💡 有关详细说明,请参阅 Aspect 文档

3.2 访问智能合约的状态变化

要将 HelloWorld 合约的状态与您的 Aspect 集成,请按照以下步骤操作:

在 aspect/index.ts 中,添加您的 Aspect 以检查交易,如果调用了 world 函数,则回滚:

import {
    allocate,
        entryPoint,
        execute,
        IPostContractCallJP,
        PostContractCallInput,
        sys,
        uint8ArrayToHex,
} from "@artela/aspect-libs";

// 1. implement IPostContractCallJP
class Aspect implements IPostContractCallJP {

    isOwner(sender: Uint8Array): bool {
// implement me
// if return false,bind、unbind、upgrade Aspect will be block
        return true;
    }
/**
     * postContractCall is a join-point which will be invoked after a contract call has finished.
     *
     * @param input input to the current join point
     */
    postContractCall(input: PostContractCallInput): void {

        let txData = uint8ArrayToHex(input.call!.data);

// if call `world` function then revert, 30b67baa is method signature of `world`
        if (txData.startsWith("30b67baa")) {
            sys.revert("the function `world` not available");
        }
    }
}

// 2.register aspect Instance
const aspect = new Aspect();
entryPoint.setAspect(aspect);

// 3.must export it
export {execute, allocate};

3.3 编译 Aspect

构建您的 Aspect:

npm run aspect:build

✅ 生成的 release.wasm 文件将在 build 文件夹中包含必要的 WASM 字节码。

3.4 部署 Aspect

部署编译后的 Aspect:

npm run aspect:deploy -- --wasm ./build/release.wasm --joinPoints PostContractCall

✅ 成功执行后,终端将显示 Aspect 地址。请务必记录此地址,因为稍后会用到。

💡 有关此命令的详细用法,请参阅 deploy-aspect 命令文档。

4. 绑定智能合约和 Aspect

部署 Aspect 并不会自动激活它。要使其生效,需要将其绑定到智能合约:

npm run contract:bind -- --contract {smart-contract-address} \
                         --abi ./build/contract/HelloWorld.abi \
                         --aspectId {aspect-Id}

将占位符 {smart-contract-address} 替换为步骤 2.3 中部署智能合约时获得的信息。将占位符 {aspect-Id} 替换为步骤 3.4 中部署 Aspect 时获得的信息。

✅ 绑定成功,会输出交易收据。

💡 有关此命令的详细用法,请参阅 bind-aspect 命令文档。

5. 测试智能合约和 Aspect 集成

现在 HelloWorld 合约和 Aspect 已绑定,调用 world 方法进行测试:

npm run contract:call -- --contract {smart-contract-address}  \
                         --abi ./build/contract/HelloWorld.abi   \
                         --method world

将占位符 {smart-contract-address} 替换为步骤 2.3 中部署智能合约时获得的信息。

✅ 由于 Aspect 拦截,交易已回滚。

image.png

恭喜!您已经学习了 Aspect 开发的基础知识。要深入了解,请参阅我们全面的 Aspect 文档。


Product

  • Learn
  • Build
  • Developer Profile
  • On-Chain Contract & Payment

Community

  • Discord
  • Twitter
  • GitHub
  • Telegram
  • LinkedIn
  • Substack

Company

  • Apply for Cooperation
  • Terms of Use Agreement
  • Privacy Policy
  • Press Kit

© 2025 OpenBuild, All rights reserved.