-
Notifications
You must be signed in to change notification settings - Fork 293
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: subscribe to a dapp with a token (#4696)
Adds a separate TS package for entrypoints Adds a contract to act as a canonical fee payment contract Adds tests for various dapp subscription and fee payment flows Co-authored-by: Mitchell Tracy <[email protected]>
- Loading branch information
Showing
56 changed files
with
103,966 additions
and
709 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
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,98 @@ | ||
pragma solidity >=0.8.18; | ||
|
||
import {IERC20} from "@oz/token/ERC20/IERC20.sol"; | ||
import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol"; | ||
|
||
// Messaging | ||
import {IRegistry} from "../../src/core/interfaces/messagebridge/IRegistry.sol"; | ||
import {IInbox} from "../../src/core/interfaces/messagebridge/IInbox.sol"; | ||
import {DataStructures} from "../../src/core/libraries/DataStructures.sol"; | ||
// docs:start:content_hash_sol_import | ||
import {Hash} from "../../src/core/libraries/Hash.sol"; | ||
// docs:end:content_hash_sol_import | ||
|
||
// docs:start:init | ||
contract GasPortal { | ||
using SafeERC20 for IERC20; | ||
|
||
IRegistry public registry; | ||
IERC20 public underlying; | ||
bytes32 public l2TokenAddress; | ||
|
||
function initialize(address _registry, address _underlying, bytes32 _l2TokenAddress) external { | ||
registry = IRegistry(_registry); | ||
underlying = IERC20(_underlying); | ||
l2TokenAddress = _l2TokenAddress; | ||
} | ||
// docs:end:init | ||
|
||
// docs:start:deposit_public | ||
/** | ||
* @notice Deposit funds into the portal and adds an L2 message which can only be consumed publicly on Aztec | ||
* @param _to - The aztec address of the recipient | ||
* @param _amount - The amount to deposit | ||
* @param _canceller - The address that can cancel the L1 to L2 message | ||
* @param _deadline - The timestamp after which the entry can be cancelled | ||
* @param _secretHash - The hash of the secret consumable message. The hash should be 254 bits (so it can fit in a Field element) | ||
* @return - The key of the entry in the Inbox | ||
*/ | ||
function depositToAztecPublic( | ||
bytes32 _to, | ||
uint256 _amount, | ||
address _canceller, | ||
uint32 _deadline, | ||
bytes32 _secretHash | ||
) external payable returns (bytes32) { | ||
// Preamble | ||
IInbox inbox = registry.getInbox(); | ||
DataStructures.L2Actor memory actor = DataStructures.L2Actor(l2TokenAddress, 1); | ||
|
||
// Hash the message content to be reconstructed in the receiving contract | ||
bytes32 contentHash = Hash.sha256ToField( | ||
abi.encodeWithSignature("mint_public(bytes32,uint256,address)", _to, _amount, _canceller) | ||
); | ||
|
||
// Hold the tokens in the portal | ||
underlying.safeTransferFrom(msg.sender, address(this), _amount); | ||
|
||
// Send message to rollup | ||
return inbox.sendL2Message{value: msg.value}(actor, _deadline, contentHash, _secretHash); | ||
} | ||
|
||
/** | ||
* @notice Cancel a public depositToAztec L1 to L2 message | ||
* @dev only callable by the `canceller` of the message | ||
* @param _to - The aztec address of the recipient in the original message | ||
* @param _amount - The amount to deposit per the original message | ||
* @param _deadline - The timestamp after which the entry can be cancelled | ||
* @param _secretHash - The hash of the secret consumable message in the original message | ||
* @param _fee - The fee paid to the sequencer | ||
* @return The key of the entry in the Inbox | ||
*/ | ||
function cancelL1ToAztecMessagePublic( | ||
bytes32 _to, | ||
uint256 _amount, | ||
uint32 _deadline, | ||
bytes32 _secretHash, | ||
uint64 _fee | ||
) external returns (bytes32) { | ||
IInbox inbox = registry.getInbox(); | ||
DataStructures.L1Actor memory l1Actor = DataStructures.L1Actor(address(this), block.chainid); | ||
DataStructures.L2Actor memory l2Actor = DataStructures.L2Actor(l2TokenAddress, 1); | ||
DataStructures.L1ToL2Msg memory message = DataStructures.L1ToL2Msg({ | ||
sender: l1Actor, | ||
recipient: l2Actor, | ||
content: Hash.sha256ToField( | ||
abi.encodeWithSignature("mint_public(bytes32,uint256,address)", _to, _amount, msg.sender) | ||
), | ||
secretHash: _secretHash, | ||
deadline: _deadline, | ||
fee: _fee | ||
}); | ||
bytes32 entryKey = inbox.cancelL2Message(message, address(this)); | ||
// release the funds to msg.sender (since the content hash (& message key) is derived by hashing the caller, | ||
// we confirm that msg.sender is same as `_canceller` supplied when creating the message) | ||
underlying.transfer(msg.sender, _amount); | ||
return entryKey; | ||
} | ||
} |
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
9 changes: 9 additions & 0 deletions
9
noir-projects/noir-contracts/contracts/app_subscription_contract/Nargo.toml
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,9 @@ | ||
[package] | ||
name = "app_subscription_contract" | ||
authors = [""] | ||
compiler_version = ">=0.18.0" | ||
type = "contract" | ||
|
||
[dependencies] | ||
aztec = { path = "../../../aztec-nr/aztec" } | ||
authwit = { path = "../../../aztec-nr/authwit" } |
84 changes: 84 additions & 0 deletions
84
noir-projects/noir-contracts/contracts/app_subscription_contract/src/dapp_payload.nr
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,84 @@ | ||
use dep::aztec::context::PrivateContext; | ||
use dep::aztec::protocol_types::{ | ||
constants::GENERATOR_INDEX__SIGNATURE_PAYLOAD, hash::pedersen_hash, traits::{Hash, Serialize}, | ||
address::AztecAddress | ||
}; | ||
|
||
use dep::authwit::entrypoint::function_call::{FunctionCall, FUNCTION_CALL_SIZE_IN_BYTES}; | ||
|
||
// FUNCTION_CALL_SIZE * DAPP_MAX_CALLS + 1 | ||
global DAPP_PAYLOAD_SIZE: Field = 5; | ||
// FUNCTION_CALL_SIZE_IN_BYTES * DAPP_MAX_CALLS + 32 | ||
global DAPP_PAYLOAD_SIZE_IN_BYTES: Field = 129; | ||
|
||
global DAPP_MAX_CALLS: Field = 1; | ||
|
||
// Note: If you change the following struct you have to update default_entrypoint.ts | ||
// docs:start:app-payload-struct | ||
struct DAppPayload { | ||
function_calls: [FunctionCall; DAPP_MAX_CALLS], | ||
nonce: Field, | ||
} | ||
// docs:end:app-payload-struct | ||
|
||
impl Serialize<DAPP_PAYLOAD_SIZE> for DAppPayload { | ||
// Serializes the entrypoint struct | ||
fn serialize(self) -> [Field; DAPP_PAYLOAD_SIZE] { | ||
let mut fields: BoundedVec<Field, DAPP_PAYLOAD_SIZE> = BoundedVec::new(0); | ||
for call in self.function_calls { | ||
fields.extend_from_array(call.serialize()); | ||
} | ||
fields.push(self.nonce); | ||
fields.storage | ||
} | ||
} | ||
|
||
impl Hash for DAppPayload { | ||
fn hash(self) -> Field { | ||
pedersen_hash( | ||
self.serialize(), | ||
GENERATOR_INDEX__SIGNATURE_PAYLOAD | ||
) | ||
} | ||
} | ||
|
||
impl DAppPayload { | ||
// Serializes the payload as an array of bytes. Useful for hashing with sha256. | ||
fn to_be_bytes(self) -> [u8; DAPP_PAYLOAD_SIZE_IN_BYTES] { | ||
let mut bytes: BoundedVec<u8, DAPP_PAYLOAD_SIZE_IN_BYTES> = BoundedVec::new(0); | ||
|
||
for i in 0..DAPP_MAX_CALLS { | ||
bytes.extend_from_array(self.function_calls[i].to_be_bytes()); | ||
} | ||
bytes.extend_from_array(self.nonce.to_be_bytes(32)); | ||
|
||
bytes.storage | ||
} | ||
|
||
// Executes all private and public calls | ||
// docs:start:entrypoint-execute-calls | ||
fn execute_calls(self, context: &mut PrivateContext, target_address: AztecAddress) { | ||
for call in self.function_calls { | ||
// whitelist the calls that the user can do only go to the expected Dapp contract | ||
assert(call.target_address == target_address); | ||
if call.is_public { | ||
context.call_public_function_with_packed_args( | ||
call.target_address, | ||
call.function_selector, | ||
call.args_hash, | ||
false, | ||
false | ||
); | ||
} else { | ||
let _result = context.call_private_function_with_packed_args( | ||
call.target_address, | ||
call.function_selector, | ||
call.args_hash, | ||
false, | ||
false | ||
); | ||
} | ||
} | ||
} | ||
// docs:end:entrypoint-execute-calls | ||
} |
Oops, something went wrong.