Use Cases¶
The following use cases were considered during the creation of this specification.
- Stand Alone Package with an Inheritable Contract
e.g.
owned
See full description.
- Dependent Package with an Inheritable Contract
e.g.
transferable
See full description.
- Stand Alone Package with a Reusable Contract
e.g.
standard-token
See full description.
- Stand Alone Package with a Deployed Contract
e.g.
safe-math-lib
See full description.
- Dependent Package with a Reusable Contract
e.g.
piper-coin
See full description.
- Stand Alone Package with a Deployed Contract Linked against a Deployed Library
e.g.
escrow
See full description.
- Dependent Package with a Deployed Contract Linked against a Package Dependency
e.g.
wallet
See full description.
- Dependent Package with a Deployed Contract Linked Against a Deep Dependency
e.g.
wallet-with-send
See full description.
Each use case builds incrementally on the previous one.
Keywords¶
Stand Alone: | Package has no external dependencies (i.e. no build_dependencies ),
contains all contract data needed without reaching into another package. |
---|---|
Dependent: | Package does not contain all necessary contract data (i.e. has build_dependencies ),
must reach into a package dependency to retrieve data. |
Inheritable: | Contract doesn’t provide useful functionality on it’s own and is meant to serve as a base contract for others to inherit from. |
Reusable: | Contract is useful on it’s own, meant to be used as-is. |
Deployed Contract/Library: | |
Refers to an instance of a contract/library that has already been deployed to a specific address on a chain. | |
Package Dependency: | |
External dependency directly referenced via the build_dependencies of a package. |
|
Deep Dependency: | |
External dependency referenced via the build_dependencies of a package dependency
(or by reaching down dependency tree as far as necessary). |
Stand Alone Package with an Inheritable Contract¶
For the first example we’ll look at a package which only contains contracts which serve as base contracts for other contracts to inherit from but do not provide any real useful functionality on their own. The common owned pattern is a example for this use case.
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.8;
contract Owned {
address owner;
modifier onlyOwner { require(msg.sender == owner); _; }
constructor() public {
owner = msg.sender;
}
}
For this example we will assume this file is located in the solidity source file./contracts/owned.sol
The owned
package contains a single solidity source source file
which is intended to be used as a base contract for other contracts to
be inherited from. The package does not define any pre-deployed
addresses for the owned contract.
The smallest Package for this example looks like this:
{
"manifest_version": "2",
"version": "1.0.0",
"package_name": "owned",
"sources": {
"./contracts/owned.sol": "ipfs://QmUjYUcX9kLv2FQH8nwc3RLLXtU3Yv5XFpvEjFcAKXB6xD"
}
}
A Package which includes more than the minimum information would look like this.
{"manifest_version":"2","meta":{"authors":["Piper Merriam <pipermerriam@gmail.com>"],"description":"Reusable contracts which implement a privileged 'owner' model for authorization.","keywords":["authorization"],"license":"MIT","links":{"documentation":"ipfs://QmUYcVzTfSwJoigggMxeo2g5STWAgJdisQsqcXHws7b1FW"}},"package_name":"owned","sources":{"./contracts/Owned.sol":"ipfs://Qme4otpS88NV8yQi8TfTP89EsQC5bko3F5N1yhRoi6cwGV"},"version":"1.0.0"}
This fully fleshed out Package is meant to demonstrate various pieces of optional data that can be included. However, for the remainder of our examples we will be using minimalistic Packages to keep our examples as succinct as possible.
Dependent Package with an Inheritable Contract¶
Now that we’ve seen what a simple package looks like, let’s see how to declare dependencies.
The next package will implement the transferable pattern and will
depend on our owned
package for the authorization mechanism to
ensure that only the contract owner may transfer ownership. The
transferable
package will contain a single solidity source file
./contracts/transferable.sol
.
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.8;
import {Owned} from "owned/Owned.sol";
contract Transferable is Owned {
event OwnerChanged(address indexed prevOwner, address indexed newOwner);
function transferOwner(address newOwner) public onlyOwner returns (bool) {
emit OwnerChanged(owner, newOwner);
owner = newOwner;
return true;
}
}
The EPM spec is designed to provide as high a guarantee as possible that builds are deterministic and reproducable. To ensure that each package you install gets the exact dependencies it needs, all dependencies are declared as content addressed URIs. This ensures that when a package manager fetches a dependency it always gets the right one.
The IPFS URI for the previous owned
Package turns out to be
ipfs://QmXDf2GP67otcF2gjWUxFt4AzFkfwGiuzfexhGuotGTLJH
which is what
we will use in our transferable
package to declare the dependency.
The Package looks like the following.
{"build_dependencies":{"owned":"ipfs://QmbeVyFLSuEUxiXKwSsEjef6icpdTdA4kGG9BcrJXKNKUW"},"manifest_version":"2","meta":{"authors":["Piper Merriam <pipermerriam@gmail.com>"],"description":"Reusable contracts which implement a privileged 'owner' model for authorization with functionality for transfering ownership.","keywords":["authorization"],"license":"MIT"},"package_name":"transferable","sources":{"./contracts/Transferable.sol":"ipfs://QmZYkdUUTwREjfy4vQc3enzu6WKk8eNyvGERqy1cNNVkAD"},"version":"1.0.0"}
It will be up to the package management software to determine how the
owned
dependency actually gets installed as well as handling any
import remappings necessary to make the import statement work.
Stand Alone Package with a Reusable Contract¶
In this next example we’ll look at a package which contains a reusable contract. This means that the package provides a contract which can be on its own in some manner. For this example we will be creating a package which includes a reusable standard ERC20 token contract.
The source code for these contracts was pulled from the SingularDTV github repository. Thanks to them for a very well written contract.
This package will contain two solidity source files:
Given that these source files are relatively large they will not be included here within the guide but can be found in the ./examples/standard-token/ directory within this repository.
Since this package includes a contract which may be used as-is, our
Package is going to contain additional information from our previous
examples, specifically, the contract_types
section. Since we expect
people to compile this contract theirselves we won’t need to include any
of the contract bytecode, but it will be useful to include the contract
ABI and Natspec information. Our Package will look something like the
following. The contract ABI and NatSpec sections have been truncated to
improve legibility. The full Package can be found
here
{
"manifest_version": "2",
"version": "1.0.0",
"package_name": "standard-token",
"sources": {
"./contracts/AbstractToken.sol": "ipfs://QmQMXDprXxCunfQjA42LXZtzL6YMP8XTuGDB6AjHzpYHgk",
"./contracts/StandardToken.sol": "ipfs://QmNLr7DzmiaQvk25C8bADBnh9bF5V3JfbwHS49kyoGGEHz"
},
"contract_types": {
"StandardToken": {
"abi": [
"..."
],
"natspec": {
"author": "Stefan George - <stefan.george@consensys.net>",
"title": "Standard token contract",
"methods": {
"allowance(address,address)": {
"details": "Returns number of allowed tokens for given address.",
"params": {
"_owner": "Address of token owner.",
"_spender": "Address of token spender."
}
},
"...": "..."
}
}
}
}
}
While it is not required to include the contract ABI and NatSpec information, it does provide those using this package with the data they would need to interact with an instance of this contract without having to regenerate this information from source.
Stand Alone Package with a Deployed Contract¶
Now that we’ve seen what a package looks like which includes a fully functional contract that is ready to be deployed, lets explore a package that also includes a deployed instance of that contract.
Solidity Libraries are an excellent example of this type of package, so
for this example we are going to write a library for safe math
operations called safe-math-lib
. This library will implement
functions to allow addition and subtraction without needing to check for
underflow or overflow conditions. Our package will have a single
solidity source file ./contracts/SafeMathLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.8;
/// @title Safe Math Library
/// @author Piper Merriam <pipermerriam@gmail.com>
library SafeMathLib {
/// @dev Adds a and b, throwing an error if the operation would cause an overflow.
/// @param a The first number to add
/// @param b The second number to add
function safeAdd(uint256 a, uint256 b) public pure returns (uint256 c) {
c = a + b;
assert(c >= a);
return c;
}
/// @dev Subtracts b from a, throwing an error if the operation would cause an underflow.
/// @param a The number to be subtracted from
/// @param b The amount that should be subtracted
function safeSub(uint256 a, uint256 b) public pure returns (uint256) {
assert(b <= a);
return a - b;
}
}
This will be our first package which includes the deployments
section which is the location where information about deployed contract
instances is found. Lets look at the Package, some parts have been
truncated for readability but the full file can be found
here
{
"manifest_version": "2",
"version": "1.0.0",
"package_name": "safe-math-lib",
"sources": {
"./contracts/SafeMathLib.sol": "ipfs://QmVN1p6MmMLYcSq1VTmaSDLC3xWuAUwEFBFtinfzpmtzQG"
},
"contract_types": {
"SafeMathLib": {
"deployment_bytecode": {
"bytecode": "0x606060405234610000575b60a9806100176000396000f36504062dabbdf050606060405260e060020a6000350463a293d1e88114602e578063e6cb901314604c575b6000565b603a600435602435606a565b60408051918252519081900360200190f35b603a6004356024356088565b60408051918252519081900360200190f35b6000828211602a57508082036081566081565b6000565b5b92915050565b6000828284011115602a57508181016081566081565b6000565b5b9291505056"
},
"runtime_bytecode": {
"bytecode": "0x6504062dabbdf050606060405260e060020a6000350463a293d1e88114602e578063e6cb901314604c575b6000565b603a600435602435606a565b60408051918252519081900360200190f35b603a6004356024356088565b60408051918252519081900360200190f35b6000828211602a57508082036081566081565b6000565b5b92915050565b6000828284011115602a57508181016081566081565b6000565b5b9291505056"
},
"abi": [
"..."
],
"compiler": {
"name": "solc",
"version": "0.4.6+commit.2dabbdf0.Darwin.appleclang",
"settings": {
"optimize": true
}
},
"natspec": {
"title": "Safe Math Library",
"author": "Piper Merriam <pipermerriam@gmail.com>",
"...": "..."
}
}
},
"deployments": {
"blockchain://41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d/block/1e96de11320c83cca02e8b9caf3e489497e8e432befe5379f2f08599f8aecede": {
"SafeMathLib": {
"contract_type": "SafeMathLib",
"address": "0x8d2c532d7d211816a2807a411f947b211569b68c",
"transaction": "0xaceef751507a79c2dee6aa0e9d8f759aa24aab081f6dcf6835d792770541cb2b",
"block": "0x420cb2b2bd634ef42f9082e1ee87a8d4aeeaf506ea5cdeddaa8ff7cbf911810c"
}
}
}
}
The first thing to point out is that unlike our standard-token
contract, we’ve included the bytecode
, runtime_bytecode
and
compiler
information in the SafeMathLib
section of the
contract_type
definition. This is because we are also including a
deployed instance of this contract and need to require adequate
information for package managers to verify that the contract found at
the deployed address is in fact from the source code included in this
package.
The next thing to look at is the deployments
section. The first
thing you should see is the
BIP122
URI.
blockchain://41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d/block/1e96de11320c83cca02e8b9caf3e489497e8e432befe5379f2f08599f8aecede
This URI defines the chain on which the SafeMathLib
library was
deployed. The first hash you see,
41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d
is
the genesis block hash for the Ropsten test network. The later hash
1e96de11320c83cca02e8b9caf3e489497e8e432befe5379f2f08599f8aecede
is
the block hash for block number 168,238 from the Ropsten chain.
Under that URI there is a single Contract Instance. It specifies that
it’s Contract Type is SafeMathLib
, the address that the contract
instance can be found at, the transaction hash for the transaction that
deployed the contract, and the block hash in which the deploying
transaction was mined.
Dependent Package with a Reusable Contract¶
For our next example we’ll be creating a package includes a deployed
instance of a Contract Type from that comes from a package dependency.
This differs from our previous safe-math-lib
example where our
deployment is referencing a local contract from the local
contract_types
. In this package we will be referencing a
contract_type
from one of the build_dependencies
We are going to use the standard-token
package we created earlier
and include a deployed version of the StandardToken
contract.
Our package will be called piper-coin
and will not contain any
source files since it merely makes use of the contracts from the
standard-token
package. The Package is listed below, and can be found at
./examples/piper-coin/1.0.0.json
{"build_dependencies":{"standard-token":"ipfs://QmVu9zuza5mkJwwcFdh2SXBugm1oSgZVuEKkph9XLsbUwg"},"deployments":{"blockchain://41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d/block/4803939cf88aaf46fb7c9fb771cda4e4072c6c5fe3aaad1860f7064ef18f50b9":{"PiperCoin":{"address":"0xC70871869Ff35e9d08e650b49F23891DB462F181","block":"0xb21dda724b6f14bcdde1db9703a5c1bfb088763acdb725be2429c1ef8ce0a4e1","compiler":{"name":"solc","settings":{"optimize":true},"version":"0.4.24+commit.e67f0147.Emscripten.clang"},"contract_type":"standard-token:StandardToken","deployment_bytecode":{"bytecode":"0x608060405234801561001057600080fd5b5060405160208061045383398101604081815291516002819055336000818152602081815285822084905583855294519294919390927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3506103d2806100816000396000f3006080604052600436106100775763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663095ea7b3811461007c57806318160ddd146100b457806323b872dd146100db57806370a0823114610105578063a9059cbb14610126578063dd62ed3e1461014a575b600080fd5b34801561008857600080fd5b506100a0600160a060020a0360043516602435610171565b604080519115158252519081900360200190f35b3480156100c057600080fd5b506100c96101d8565b60408051918252519081900360200190f35b3480156100e757600080fd5b506100a0600160a060020a03600435811690602435166044356101de565b34801561011157600080fd5b506100c9600160a060020a03600435166102c9565b34801561013257600080fd5b506100a0600160a060020a03600435166024356102e4565b34801561015657600080fd5b506100c9600160a060020a036004358116906024351661037b565b336000818152600160209081526040808320600160a060020a038716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60025481565b600160a060020a03831660009081526020819052604081205482118015906102295750600160a060020a03841660009081526001602090815260408083203384529091529020548211155b80156102355750600082115b156102be57600160a060020a0380841660008181526020818152604080832080548801905593881680835284832080548890039055600182528483203384528252918490208054879003905583518681529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016102c2565b5060005b9392505050565b600160a060020a031660009081526020819052604090205490565b3360009081526020819052604081205482118015906103035750600082115b15610373573360008181526020818152604080832080548790039055600160a060020a03871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a35060016101d2565b5060006101d2565b600160a060020a039182166000908152600160209081526040808320939094168252919091522054905600a165627a7a72305820cf9d6a3f751ca1e6b9bc2324e42633a4cde513d64c3e6cc32d6359629249e90200290000000000000000000000000000000000000000000000000000000000000001"},"runtime_bytecode":{"bytecode":"0x6080604052600436106100775763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663095ea7b3811461007c57806318160ddd146100b457806323b872dd146100db57806370a0823114610105578063a9059cbb14610126578063dd62ed3e1461014a575b600080fd5b34801561008857600080fd5b506100a0600160a060020a0360043516602435610171565b604080519115158252519081900360200190f35b3480156100c057600080fd5b506100c96101d8565b60408051918252519081900360200190f35b3480156100e757600080fd5b506100a0600160a060020a03600435811690602435166044356101de565b34801561011157600080fd5b506100c9600160a060020a03600435166102c9565b34801561013257600080fd5b506100a0600160a060020a03600435166024356102e4565b34801561015657600080fd5b506100c9600160a060020a036004358116906024351661037b565b336000818152600160209081526040808320600160a060020a038716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a35060015b92915050565b60025481565b600160a060020a03831660009081526020819052604081205482118015906102295750600160a060020a03841660009081526001602090815260408083203384529091529020548211155b80156102355750600082115b156102be57600160a060020a0380841660008181526020818152604080832080548801905593881680835284832080548890039055600182528483203384528252918490208054879003905583518681529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016102c2565b5060005b9392505050565b600160a060020a031660009081526020819052604090205490565b3360009081526020819052604081205482118015906103035750600082115b15610373573360008181526020818152604080832080548790039055600160a060020a03871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a35060016101d2565b5060006101d2565b600160a060020a039182166000908152600160209081526040808320939094168252919091522054905600a165627a7a72305820cf9d6a3f751ca1e6b9bc2324e42633a4cde513d64c3e6cc32d6359629249e9020029"},"transaction":"0x93f6c5fbdb4c62f1bff8bace3ec2f80267b6cdd3710c36f9b1445031175d9f6f"}}},"manifest_version":"2","package_name":"piper-coin","version":"1.0.0"}
Most of this should be familiar but it’s worth pointing out how we
reference Contract Types from dependencies. Under the PiperCoin
entry within the deployments you should see that the contract_type
key is set to standard-token:StandardToken
. The first portion
represents the name of the package dependency within the
build_dependencies
that should be used. The later portion indicates
the contract type that should be used from that dependencies contract
types.
Stand Alone Package with a Deployed Contract Linked against a Deployed Library¶
In the previous safe-math-lib
package we demonstrated what a package
with a deployed instance of one of it’s local contracts looks like. In
this example we will build on that concept with a package which includes
a library and a contract which uses that library as well as deployed
instances of both.
The package will be called escrow
and will implementing a simple
escrow contract. The escrow contract will make use of a library to
safely send ether. Both the contract and library will be part of the
package found in the following two solidity source files.
The full source for these files can be found here: ./examples/escrow/.
The Package is listed below with some sections truncated for improved readability. The full Package can be found at ./examples/escrow/1.0.0.json
{
"manifest_version": "2",
"version": "1.0.0",
"package_name": "escrow",
"sources": {
"./contracts/SafeSendLib.sol": "ipfs://QmcnzhWjaV71qzKntv4burxyix9W2yBA2LrJB4k99tGqkZ",
"./contracts/Escrow.sol": "ipfs://QmSwmFLT5B5aag485ZWvHmfdC1cU5EFdcqs1oqE5KsxGMw"
},
"contract_types": {
"SafeSendLib": {
"...": "..."
},
"Escrow": {
"...": "..."
}
},
"deployments": {
"blockchain://41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d/block/e76cf1f29a4689f836d941d7ffbad4e4b32035a441a509dc53150c2165f8e90d": {
"SafeMathLib": {
"contract_type": "SafeSendLib",
"address": "0x80d7f7a33e551455a909e1b914c4fd4e6d0074cc",
"transaction": "0x74561167f360eaa20ea67bd4b4bf99164aabb36b2287061e86137bfa0d35d5fb",
"block": "0x46554e3cf7b768b1cc1990ad4e2d3a137fe9373c0dda765f4db450cd5fa64102"
},
"Escrow": {
"contract_type": "Escrow",
"address": "0x35b6b723786fd8bd955b70db794a1f1df56e852f",
"transaction": "0x905fbbeb6069d8b3c8067d233f58b0196b43da7a20b839f3da41f69c87da2037",
"block": "0x9b39dcab3d665a51755dedef56e7c858702f5817ce926a0cd8ff3081c5159b7f",
"runtime_bytecode": {
"link_dependencies": [
{
"offsets": [
262,
412
],
"type": "reference",
"value": "SafeSendLib"
}
]
}
}
}
}
}
This Package is the first one we’ve seen thus far that include the
link_dependencies
section within one of the Contract Instances.
The runtime_bytecode
value for the Escrow
contract has been
excluded from the example above for readability, but the full value is
as follows (wrapped to 80 characters).
0x606060405260e060020a600035046366d003ac811461003457806367e404ce1461005d5780636
9d8957514610086575b610000565b3461000057610041610095565b60408051600160a060020a03
9092168252519081900360200190f35b34610000576100416100a4565b60408051600160a060020
a039092168252519081900360200190f35b34610000576100936100b3565b005b600154600160a0
60020a031681565b600054600160a060020a031681565b60005433600160a060020a03908116911
6141561014857600154604080516000602091820152815160e260020a6324d048c7028152600160
a060020a03938416600482015230909316316024840152905173000000000000000000000000000
000000000000092639341231c926044808301939192829003018186803b156100005760325a03f4
1561000057506101e2915050565b60015433600160a060020a039081169116141561002f5760008
05460408051602090810193909352805160e260020a6324d048c7028152600160a060020a039283
1660048201523090921631602483015251730000000000000000000000000000000000000000926
39341231c9260448082019391829003018186803b156100005760325a03f41561000057506101e2
915050565b610000565b5b5b56
This bytecode is unlinked, meaning it has missing portions of data, populated
instead with zeroes (0000000000000000000000000000000000000000
).
This section of zeroes is referred to as a Link Reference. The
entries in the link_dependencies
section of a Contract Instance
describe how these link references should be filled in.
The offsets
value specifies the locations (as byte offset) where the
replacement should begin. The value
defines what should be used to replace
the link reference. In this case, the value
is referencing the
SafeSendLib
contract instance from this Package.
Dependent Package with a Deployed Contract Linked against a Package Dependency¶
Now that we’ve seen how we can handle linking dependencies that rely on deployed Contract Instances from the local package we’ll explore an example with link dependencies that rely on contracts from the package dependencies.
In this example we’ll be writing the wallet
package which includes a
wallet contract which makes use of the previous safe-math-lib
package. We will also make use of the owned
package from our very
first example to handle authorization. Our package will contain a single
solidity source file
./contracts/Wallet.sol, shown below.
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.8;
import {SafeMathLib} from "./safe-math-lib/SafeMathLib.sol";
import {Owned} from "./owned/Owned.sol";
/// @title Contract for holding funds in escrow between two semi trusted parties.
/// @author Piper Merriam <pipermerriam@gmail.com>
contract Wallet is Owned {
using SafeMathLib for uint;
mapping (address => uint) allowances;
/// @dev Fallback function for depositing funds
fallback() external {
}
/// @dev Sends the recipient the specified amount
/// @notice This will send the reciepient the specified amount.
function send(address payable recipient, uint value) public onlyOwner returns (bool) {
return recipient.send(value);
}
/// @dev Sets recipient to be approved to withdraw the specified amount
/// @notice This will set the recipient to be approved to withdraw the specified amount.
function approve(address recipient, uint value) public onlyOwner returns (bool) {
allowances[recipient] = value;
return true;
}
/// @dev Lets caller withdraw up to their approved amount
/// @notice This will withdraw provided value, deducting it from your total allowance.
function withdraw(uint value) public returns (bool) {
allowances[msg.sender] = allowances[msg.sender].safeSub(value);
if (!msg.sender.send(value))
revert();
return true;
}
}
The Package for our wallet
package can been seen below. It has been
trimmed to improve readability. The full Package can be found at
./examples/wallet/1.0.0.json
{
"manifest_version": "2",
"version": "1.0.0",
"package_name": "wallet",
"sources": {
"./contracts/Wallet.sol": "ipfs://QmYKibsXPSTR5UjywQHX8SM4za1K3QHadtFGWmZqGA4uE9"
},
"contract_types": {
"Wallet": {
"deployment_bytecode": {
"...": "..."
},
"runtime_bytecode": {
"...": "..."
},
"...": "..."
}
},
"deployments": {
"blockchain://41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d/block/3ececfa0e03bce2d348279316100913c42ca2dcd51b8bc8d2d87ef2dc6a479ff": {
"Wallet": {
"contract_type": "Wallet",
"address": "0xcd0f8d7dab6c682d3726693ef3c7aaacc6431d1c",
"transaction": "0x5c113857925ae0d866341513bb0732cd799ebc1c18fcec253bbc41d2a029acd4",
"block": "0xccd130623ad3b25a357ead2ecfd22d38756b2e6ac09b77a37bd0ecdf16249765",
"runtime_bytecode": {
"link_dependencies": [
{
"offsets": [
340
],
"type": "reference",
"value": "safe-math-lib:SafeMathLib"
}
]
}
}
}
},
"build_dependencies": {
"owned": "ipfs://QmUwVUMVtkVctrLDeL12SoeCPUacELBU8nAxRtHUzvtjND",
"safe-math-lib": "ipfs://QmfUwis9K2SLwnUh62PDb929JzU5J2aFKd4kS1YErYajdq"
}
}
Just like our previous example, the runtime_bytecode
has been
omitted for improved readability, but the full value is as follows
(wrapped to 80 characters).
0x606060405236156100355760e060020a6000350463095ea7b381146100435780632e1a7d4d146
1006a578063d0679d341461008e575b34610000576100415b5b565b005b34610000576100566004
356024356100b5565b604080519115158252519081900360200190f35b346100005761005660043
56100f8565b604080519115158252519081900360200190f35b3461000057610056600435602435
6101da565b604080519115158252519081900360200190f35b6000805433600160a060020a03908
1169116146100d157610000565b50600160a060020a038216600090815260016020819052604090
91208290555b5b92915050565b600160a060020a033316600090815260016020908152604080832
0548151830184905281517fa293d1e8000000000000000000000000000000000000000000000000
0000000081526004810191909152602481018590529051730000000000000000000000000000000
0000000009263a293d1e89260448082019391829003018186803b156100005760325a03f4156100
00575050604080518051600160a060020a033316600081815260016020529384209190915592508
4156108fc0291859190818181858888f1935050505015156101d157610000565b5060015b919050
565b6000805433600160a060020a039081169116146101f657610000565b604051600160a060020
a0384169083156108fc029084906000818181858888f19450505050505b5b9291505056
As you can see, this bytecode contains missing link references, in this case to
the SafeMathLib
library from the safe-math-lib
package dependency.
If you look in the link_dependencies
section of our Wallet
contract you’ll see it’s items are similar to the ones from our previous
example.
{
"link_dependencies": [
{
"offsets": [
340
],
"type": "reference",
"value": "safe-math-lib:SafeMathLib"
}
]
}
However, unlike the previous example which linked against a local
contract type, value
portion is prefixed with the name of the
package which contains the address of the contract instance that this
should be linked against.
Dependent Package with a Deployed Contract Linked against a Deep Dependency¶
In the previous example we saw how link dependencies against direct package dependencies are handled. In this example we will look at how this works when the link dependency is against a package deeper in the dependency tree.
This package will contain a single solidity source file
./contracts/WalletWithSend.sol
which extends our previous Wallet
contract, adding a new
approvedSend
function.
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.8;
import {Wallet} from "./wallet/Wallet.sol";
/// @title Wallet contract with simple send and approval spending functionality
/// @author Piper Merriam <pipermerriam@gmail.com>
contract WalletWithSend is Wallet {
/// @dev Sends funds that have been approved to the specified address
/// @notice This will send the reciepient the specified amount.
function approvedSend(uint value, address payable to) public {
allowances[msg.sender] = allowances[msg.sender].safeSub(value);
if (!to.send(value))
revert();
}
}
This new approvedSend
function allows spending an address’s provided
allowance by sending it to a specified address.
The Package for our wallet-with-send
package can been seen below. It
has been trimmed to improve readability. The full Package can be found
at
./examples/wallet-with-send/1.0.0.json
{
"manifest_version": "2",
"version": "1.0.0",
"package_name": "wallet-with-send",
"sources": {
"./contracts/WalletWithSend.sol": "ipfs://QmWAKLzXaxES3tszDXDPP9xvf7xqsB9FU3W7MtapQ47naU"
},
"contract_types": {
"WalletWithSend": {
"deployment_bytecode": {
"...": "..."
},
"runtime_bytecode": {
"...": "..."
},
"...": "..."
}
},
"deployments": {
"blockchain://41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d/block/bfb631d770940a93296e9b93f034f9f920ae311a8b37acd57ff0b55605beee73": {
"Wallet": {
"contract_type": "WalletWithSend",
"address": "0x7817d93f681a72758335398913136069c945d34b",
"transaction": "0xe37d5691b58472f9932545d1d44fc95463a69adb1a3da5b06da2d9f3ff5c2939",
"block": "0x5479e2f948184e13581d13e6a3d5bd5e5263d898d4514c5ec6fab37e2a1e9d6c",
"runtime_bytecode": {
"link_dependencies": [
{
"offsets": [
383,
587
],
"type": "reference",
"value": "wallet:safe-math-lib:SafeMathLib"
}
]
}
}
}
},
"build_dependencies": {
"wallet": "ipfs://QmSg2QvGhQrYgQqbTGVYjGmF9hkEZrxQNmSXsr8fFyYtD4"
}
}
The important part of this Package is the link_dependencies
section.
{
"link_dependencies": [
{
"offsets": [
383,
587
],
"type": "reference",
"value": "wallet:safe-math-lib:SafeMathLib"
}
]
}
The value
portion here means that the bytecode for this contract is
linked against the SafeMathLib
deployed instance from the
safe-math-lib
dependency from the wallet
dependency of this
package. This defines the traversal path through the dependency tree to
the deployed instance of the SafeMathLib
library which was used for
linking during deployment.