From ec833217a0384693dcd32e19e4480a58903548b5 Mon Sep 17 00:00:00 2001 From: Min Qian Lu <59981815+minqianlu@users.noreply.github.com> Date: Fri, 13 Oct 2023 16:35:32 -0400 Subject: [PATCH] slight restructure --- .../Assets/asset-configurations.md | 12 +- .../KeypomProtocol/Assets/function-call.md | 70 +++++------ .../Assets/time-customization.md | 110 ++++++++++++++++++ docs/Concepts/KeypomProtocol/terminology.md | 5 + sidebars.js | 1 + 5 files changed, 156 insertions(+), 42 deletions(-) create mode 100644 docs/Concepts/KeypomProtocol/Assets/time-customization.md diff --git a/docs/Concepts/KeypomProtocol/Assets/asset-configurations.md b/docs/Concepts/KeypomProtocol/Assets/asset-configurations.md index 493608c..b04b7aa 100644 --- a/docs/Concepts/KeypomProtocol/Assets/asset-configurations.md +++ b/docs/Concepts/KeypomProtocol/Assets/asset-configurations.md @@ -1,4 +1,14 @@ --- sidebar_label: 'Asset Configurations' --- -# Asset Configurations \ No newline at end of file +import Admonition from '@theme/Admonition'; + +# Asset Configurations + + +For every use on an Access Key, the different types of assets can be mixed to create an infinite number of experiences! + + +```rust reference +https://github.com/keypom/keypom/blob/8f9f8df397cb8cabbda30d1ddffdcddc4a733274/contract/src/models/config.rs#L16-L31 +``` \ No newline at end of file diff --git a/docs/Concepts/KeypomProtocol/Assets/function-call.md b/docs/Concepts/KeypomProtocol/Assets/function-call.md index 5cc82e0..5416c26 100644 --- a/docs/Concepts/KeypomProtocol/Assets/function-call.md +++ b/docs/Concepts/KeypomProtocol/Assets/function-call.md @@ -11,12 +11,12 @@ contract to be executed (with some exceptions). In addition, there are a huge va ## How does it work? -When an a key use containing a Function Call asset is claimed, Keypom will make the predetermined function call. This call works as any normal function call and has a preset receiver, method, and attached deposit but can include user specified arguments, immutable Keypom populated basis-of-truth arguments. +When an a key use containing a Function Call asset is claimed, Keypom will make the predetermined function call. This call works as any normal function call and has a preset receiver, method, and attached deposit. Beyond these predefined aspects, Function Call assets can also include dynamically defined arguments such as user inputs, and immutable Keypom basis-of-truth arguments. ## Structure -A Function Call Asset is defined by a vector of `MethodData`, which indicates a vector of function calls. Each `MethodData` represents a single function call, meaning that since an FC asset is defined with a vector of `MethodData`, multiple function calls can be made in a single key use. +A Function Call Asset is defined by a **vector of function calls**, which is *represented by a vector of `MethodData`*. Each `MethodData` represents a single function call. Since a Function Call asset is defined with a vector of `MethodData`, multiple function calls can be made in a single key use. The `MethodData` object outlines the following: @@ -52,22 +52,38 @@ https://github.com/keypom/keypom-js/blob/e8c43f4219a79afb3c367296cc90b8d5de97794 For **every Function Call Asset**, you can specify a *vector* of `MethodData` which allows you to execute multiple function calls for each asset. These calls are scheduled 1 by 1 using a simple for loop. This means that most of the time, the function calls will be executed in the order specified in the vector but it is not *guaranteed*. ## Injecting Keypom Arguments -Prior to calling the function defined in each `MethodData`, the Keypom contract will **automatically** modify the outgoing arguments according to the defined `keypom_args`. +Injected Keypom Arguments are arguments that are automatically populated into a function call's outgoing arguments. These arguments are immutable and cannot be spoofed, meaning they allow receiving contracts to use them as a source of truth for certain pieces of information. The following arguments can be injected automatically by Keypom: -For example, if the `account_id_field` is set to `"claiming_account"`, then Keypom will inject the claiming account's IDs into the `claiming_account` field in the args. In addition, arguments can be injected into nested fields by using object dot notation; `funder_id_field: "creator.account_id"` will inject the drop funder's account ID into the `creator` object under the field `account_id`. If specified fields do not exist, Keypom will create them. +* **Claiming Account ID**: This is the account ID that is claiming that particular key use. +* **Drop ID**: This is a unique identifier for each drop. +* **Drop Funder Account ID**: The account ID of the account that created the drop, this can be useful for ensuring only trusted users can trigger certain behaviours in your receiving contract +* **Key ID**: Each key in a drop is identified with an integer key ID. This is **unique in to that specific drop** but not accross multiple different drops. -When a key is used and a function is called, an data structure is **automatically** attached to the arguments, known as the `keypom_args`. It contains the information that the drop creator specified in the `MethodData.keypom_args`. + Prior to function execution, the Keypom contract will **automatically** modify the outgoing arguments according to the defined `keypom_args`. The desired location for these arguments to be injected can be specified using the following data structure. These locations must be specified at drop creation and cannot be changed. ```rust reference https://github.com/keypom/keypom/blob/8f9f8df397cb8cabbda30d1ddffdcddc4a733274/contract/src/assets/function_call/models.rs#L32-L45 ``` +For example, if the `account_id_field` is set to `"claiming_account"`, then Keypom will inject the claiming account's IDs into the `claiming_account` field in the args. -This additional data structure allows receiving contracts to protect themselves from malicious calls by ensuring that critical pieces of information are correct. For example, if a contract knows that `claiming_account_id` should be automatically injected and only wants to work with drops from a particular funder `moon.near`, they can check the `keypom_args`. +Arguments can be injected into nested fields by using object dot notation; `funder_id_field: "creator.account_id"` will inject the drop funder's account ID into the `creator` object under the field `account_id`. If specified fields do not exist, Keypom will create them. Attempting to nest into a non-object will cause the drop creation to fail. -To ensure that `claiming_account_id` is being injected by Keypom rather than hardcoded, the receiving contract can check that `keypom_args.account_id_field` is set to `claiming_account_id`. +### Protect your Contracts and Validate Information with Keypom Args +Take the perspective of the receiver contract form the previous examples. If you expect a certain funder account ID under the `creator` field, how do you know that this value has actually been injected by Keypom and not maliciously hardcoded? That is, how can you know that somebody else didn't create a malicious drop and hardcode the `creator` field to the expected value? -To make sure that all claims come from a drop funded by `moon.near`, the drop can inject the funder's account ID using `keypom_args` into any arbitrary field such as `drop_funder`. The receiver contract can then check the value `drop_funder` is `moon.near`, **and** check that `keypom_args.funder_id_field` is set to `drop_funder`. +Here, `keypom_args` comes in handy. When a key is used and a function is called, a seperate data structure is **automatically** attached to the arguments, known as the `keypom_args`. This object contains informs the receiver contract of the arguments that have been automatically injected and the location in which they were injected. This is information that the drop creator specified in the `MethodData.keypom_args` during drop creation. + +This additional data structure allows receiving contracts to protect themselves from malicious calls by ensuring that critical pieces of information are correct. For example, if a contract + +* Knows that the field `claiming_account_id` should contain information automatically injected by Keypom +* Only wants to work with drops from a particular funder `moon.near` + +They can leverage the `keypom_args` in the following manner. + +To ensure that `claiming_account_id` is being injected by Keypom rather than hardcoded, the receiving contract can check that `keypom_args.account_id_field` is set to `claiming_account_id`. Since Keypom injected arguments cannot be overriden, you know with 100% certainty that whatever value you read in the `claiming_account_id` has been injected by Keypom. + +To make sure that all claims come from a drop funded by `moon.near`, a similar strategy can be employed but this time while also checking the value. The drop can first inject the funder's account ID using `keypom_args` into any arbitrary field such as `creator`. The receiver contract can then check the value `creator` is `moon.near`, **and** check that `keypom_args.funder_id_field` is set to `creator`. If both checks pass, you know with 100% certainty that Keypom populated the `creator` field and that the drop funder was `moon.near` :::info The receiver contract's validation of injected arguments per the examples given above looks like this @@ -75,12 +91,12 @@ The receiver contract's validation of injected arguments per the examples given ```rust #[payable] pub fn myFunction(&mut self, mint_id: String, claiming_account_id: String, drop_funder: data, keypomArgs: keypom_args) -> Promise { - // Ensure arguments are correct if predecessor is Keypom + // If the function call is coming from Keypom, validate arguments if(env::predecessor_account_id() == "v3.keypom.near"){ //Ensure claiming_account_id was not hardcoded assert!(keypomArgs.account_id_field == "claiming_account_id", "Call must come from valid Keypom drop"); - // Ensure drop_funder is moon.near and not hardcoded + // Ensure drop_funder is moon.near AND is not hardcoded assert!(drop_funder == "moon.near" && keypomArgs.funder_id_field == "drop_funder", "Call must come from valid Keypom drop"); } ... @@ -89,34 +105,6 @@ pub fn myFunction(&mut self, mint_id: String, claiming_account_id: String, drop_ ::: -### Motivation -By using these injected arguments as a source-of-truth, you can easily create **both** exclusivity and security in your Keypom experiences. - -Let's say there was an exclusive NFT contract that allowed the Keypom contract to mint NFTs as part of an FC drop. Only Keypom -was given access to mint the NFTs so they could be given out as linkdrops. The organizer only wanted links that were part of their -drop to be valid. For this reason, the NFT contract would only mint if Keypom called the `nft_mint` function and there was a field -`series` passed in and it was equal to the drop ID created by the organizer. - -Let's say the owner created an exclusive drop that happened to have a drop ID of 5. They could then go to the NFT contract -and restrict NFTs to only be minted if: -- `series` had a value of 5. -- The Keypom contract was the one calling the function. - -In order for this to work, when creating the drop, the owner would need to specify that the `drop_id_field` was set to a value of `series` -such that the drop ID is correctly passed into the function. - -The problem with this approach is that the NFT contract has no way of knowing which arguments were sent by the **user** when the drop -was created as part of the MethodData `args` and which arguments are automatically populated by the Keypom contract. There is nothing -stopping a malicious user from creating a new drop that has an ID of 6 but hard-coding in the actual arguments that `series` should have -a value of 5. In this case, the malicious drop would have *no* `drop_id_field` and the NFT contract would have no way of knowing that the -`series` value is malicious. - -This can be prevented if a new field is introduced representing what was automatically injected by the Keypom contract itself. At the -end of the day, Keypom will **always** send correct information to the receiving contracts. If those contracts have a way to know what has -been sent by Keypom and what has been manually set by users, the problem is solved. In the above scenario, the NFT contract would simply add -an assertion that the `keypom_args` had the `account_id_field` set to `Some(series)` meaning that the incoming `series` field was set by Keypom -and not by a malicious user. - ## Prohibited Methods Since all function calls will be signed by the Keypom contract, there are a few restrictions in place to avoid malicious behaviors. @@ -134,7 +122,7 @@ from calling private methods through FC assets. Function call assets are the bread and butter of the Keypom contract. They are the most powerful and complex assets that can currently be created. With this complexity, there are an almost infinite number of use-cases that arise. -### Proof of Attendance Protocols +### Proof of Attendance Protocols [➜](../../../Cookbook/drops/fc.md#attaching-nfts-to-your-fc-drop) A very common use case in the space is what's known as Proof of Attendance. Often times when people go to events, they want a way to prove that they were there. Some traditional approaches would be to submit your wallet address and you would be sent an NFT or some other form of @@ -147,7 +135,7 @@ be lazy minted on-demand such that storage isn't paid up-front for all the token At this point, the event organizers or the funder can distribute links to people that attend the event in-person. These links would then be claimed by users and they would receive the proof of attendance. -### Auto Registration into DAOs +### Auto Registration into DAOs [➜](../../../Tutorials/Advanced/daos/introduction.md) DAOs are a raging topic in crypto. The problem with DAOs, however, is there is a barrier to entry for users that aren't familiar with the specific chain they're built on top of. Users might not have wallets or understand how to interact with contracts. On the contrary, they @@ -171,7 +159,7 @@ accomplishing multisig transactions with zero barrier to entry. The users don't even need to create a new account. They can simply call `claim` when the link is clicked which will fire the cross-contract call to the multisig contract and pass in the keypom arguments that will be cross-checked by that contract. -### NFT Ticketing +### NFT Ticketing [➜](../../../Tutorials/Advanced/ticketing/introduction.md) The problem with current NFT ticketing systems is that they require users to have a wallet. This is a huge barrier to entry for people that are attending events but don't have wallets. In addition, there is often no proof of attendance for the event as the NFT is burned in order diff --git a/docs/Concepts/KeypomProtocol/Assets/time-customization.md b/docs/Concepts/KeypomProtocol/Assets/time-customization.md new file mode 100644 index 0000000..64f238f --- /dev/null +++ b/docs/Concepts/KeypomProtocol/Assets/time-customization.md @@ -0,0 +1,110 @@ +--- +sidebar_label: 'Time Configurations' +--- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Time Configurations +Time configurations are particularly useful in defining limits on ***when*** a key may be used. This can have a wide range of applications from [Subscriptions](../../../Tutorials/Advanced/subscriptions/introduction.md) to [Ticketing](../../../Tutorials/Advanced/ticketing/introduction.md). + + + + +```rust reference +https://github.com/keypom/keypom/blob/8f9f8df397cb8cabbda30d1ddffdcddc4a733274/contract/src/models/config.rs#L35-L54 +``` + + + + +```ts reference +https://github.com/keypom/keypom-js/blob/29c10f949f02f673d4a3cecc21b0f74bca600075/src/lib/types/drops.ts#L87-L112 +``` + + + + +:::tip +All these time parameters are measured in non-leap-nanoseconds and can be tricky to work with. An example struct has been provided [below](#example-time-configuration). +::: + + +--- + +## Start +*Default: none, Keys can be used anytime* + +`Start` time is useful for any drops where you intend to restrict access until a certain time. Setting a `start` time effectively sets an activation time; a time where the keys become usable afterwards. Before a `start` time is reached, if a user tries to `claim` the key or use `create_account_and_claim`, it will not work. + +### Use case +Let's say you are running a concert, and you want to give fans exclusive access using preferred and general admission tickets. Those with preferred admission tickets can enter anytime, even when the band is setting up. General admission, on the other hand, may only enter once the band is set up. + +In this case, you would hand out general admission tickets with a start time in the time configurations and another set of preferred admission tickets with an earlier start time in the time configurations. + +To do this, you would need to create two drops, one for preferred admission and another for general admission. This is because, as covered in the [Drop Configurations Introduction](../GithubReadme/TypesOfDrops/customization-homepage.md), one set of configurations will apply to **all** keys in that drop. + +--- + +## End +*Default: none, Keys can be used anytime* + +The end parameter acts as a deactivation time. This means that once the end time is reached, all the keys in the drop will be deactivated and can no longer be used. + +### Use case +Let's pretend that you are at NEARCON representing an NFT marketplace looking to onboard users onto your platform. Your strategy is to offer an exclusive NFT to users that sign up during NEARCON. + +To do this, you hand out QR codes with an NFT drop embedded in the QR code during the event. To ensure that the users sign up *during* NEARCON, you set the drop configuration's `end` parameter to be the end of NEARCON. + +--- + +## Throttle +*Default: none, Keys can be used anytime* + +The `throttle` parameter controls how much time must pass between key uses. This works great if you want to control how frequently somebody is able to claim their assets. + +### Use case +Pretend you are running an NFT raffle for your latest creation, the MoonNFT. For this raffle, the 20th person claiming their key will be the winner and will receive their own personalized MoonNFT. + +To protect again spam and ensure a fair playing field, you can configure the drop to have a 5 minute cooldown using the `throttle` parameter. This way, a contestant cannot spam claim the key to increase their odds of winning. + +--- + +## Interval +*Default: none, Keys can be used anytime* + +The `interval` parameter is similar to the `throttle` parameter but uses the `start` time as a constant reference. This means if `interval` is every week, the key will become useable on the same day every week, regardless of when the last key use was. + +### Use case +Pretend you have a subscription to Moon's **weekly** dog biscuit delivery service that charges you every Monday. Due to all the horror stories of data breaches and identity theft, you now no longer give out your credit card information. For this reason, you wish to pay with $NEAR but the current linkdrop standard does not allow for a subscription model. + +With Keypom, you can give Moon a multi-use simple drop with a `throttle` parameter set to 2 weeks. This way, Moon will only be able to claim every week, making it a subscription. This is also beneficial in giving a sense of security to Moon, as they can claim later than Monday and know that they will be able to claim again next Monday. + +If, for one week, Moon forgets to claim, he would be able to claim twice the next week. + + +The massive benefit here is that you can have a subscription service in the NEAR ecosystem and never need to expose any of your private information. + + +--- + +## Example Time Configuration + +```ts +const ONE_SECOND_NS = 1e9; + +time: { + // Start time is 30 seconds from now + start: (Date.now() * 1000000) + ONE_SECOND_NS * 30, + + // End time is 5 minutes from start time + end: (Date.now() * 1000000) + ONE_SECOND_NS * 330, + + // Time between use is 15 seconds + throttle: ONE_SECOND_NS * 15, + + // Time after start for first use is 15 seconds + interval: ONE_SECOND_NS * 15, +} +``` + + diff --git a/docs/Concepts/KeypomProtocol/terminology.md b/docs/Concepts/KeypomProtocol/terminology.md index 52e2f53..50508b1 100644 --- a/docs/Concepts/KeypomProtocol/terminology.md +++ b/docs/Concepts/KeypomProtocol/terminology.md @@ -25,6 +25,11 @@ ___ Assets can be NEAR, Fungible Tokens, Non-Fungible Tokens, or FunctionCalls. Each key in a drop holds the same assets, meaning a user can receive any key from the drop and be guarenteed the same set of assets. + +For every use on an Access Key, the different types of assets can be mixed to create an infinite number of experiences! + + + Access Keys can have multiple uses and their assets are defined **per use**. These can be mix-and-matched to craft any experience you want. An example can be seen below: diff --git a/sidebars.js b/sidebars.js index 4da140d..b2df210 100644 --- a/sidebars.js +++ b/sidebars.js @@ -36,6 +36,7 @@ const sidebars = { 'Concepts/KeypomProtocol/Assets/basic-assets', 'Concepts/KeypomProtocol/Assets/function-call', 'Concepts/KeypomProtocol/Assets/asset-configurations', + 'Concepts/KeypomProtocol/Assets/time-customization', ], }, 'Concepts/KeypomProtocol/nft-keys',