
WT Smart Contracts
Smart contracts of the Winding Tree platform.
Documentation

Generated documentation is in the docs
folder and can be generated by running npm run soldoc
.
There are two main groups of users in the Winding Tree platform - content producers (e. g. Hotels, Airlines)
and content consumers (e. g. OTAs (Online Travel Agencies)).
Content producers
When a producer wants to participate, they have to do the following:
- Locate Winding Tree Entrypoint address
- Prepare off-chain data conforming to the specification
- Create their organization smart contract (commonly referred to as 0xORG)
- Fully custom
- Create an implementation of
OrganizationInterface
smart contract. - Deploy the custom implementation.
- Assisted
- Locate Organization Factory address from Winding Tree Entrypoint
- Call
create
method on the Organization Factory with the URI of off-chain data.
Organization smart contract that belongs to the transaction sender is created.
- Locate the appropriate Segment Directory address
- Add their newly created 0xORG to the segment directory by calling the
add
method
The Organization created in OrganizationFactory
uses the
Upgradeability Proxy pattern. In short, the Factory owner will keep
the ownership of the contract logic (proxy), whereas the transaction sender will keep the ownership of the
data. Thus the Factory owner is responsible for the code. It is possible to transfer the proxy ownership
to another account.
In any case, every Organization can have many associated keys. An associated key is an Ethereum address
registered in the Organization that can operate on behalf of the organization. That means
that for example, the associated key can sign messages on behalf of the Organization. This
is handy when providing guarantees, proving data integrity or disclosing identity.
Content consumers
When a consumer wants to participate, they have to do the following:
- Locate Winding Tree Entrypoint address
- Locate the appropriate Segment Directory address
- Call
getOrganizations
on the Segment Directory. - Call
getOrgJsonUri
on every non-zero address returned as an instance of OrganizationInterface
and crawl the off-chain data
for more information.
If a signed message occurs somewhere in the platform, a content consumer might want to decide
if it was signed by an account associated with the declared Organization. That's when they would
first verify the signature and obtain an address of the signer. In the next step, they have to verify
that the actual signer is registered as an associatedKey
with the Organization by checking its smart contract.
Requirements
Node 10 is required for running the tests and contract compilation.
Installation
npm install @windingtree/wt-contracts
import Organization from '@windingtree/wt-contracts/build/contracts/Organization.json';
import { OrganizationInterface, AbstractSegmentDirectory } from '@windingtree/wt-contracts';
Development
git clone https://github.com/windingtree/wt-contracts
nvm install
npm install
npm test
You can run a specific test with npm test -- test/segment-directory.js
or you can generate a coverage report with npm run coverage
.
Warning: We are not using the zos.json
in tests, rather zos.test.json
. If you are
getting the Cannot set a proxy implementation to a non-contract address
error, its probably
because the contract is not inzos.test.json
.
Flattener
A flattener script is also available. npm run flattener
command
will create a flattened version without imports - one file per contract.
This is needed if you plan to use tools like etherscan verifier
or securify.ch.
Deployment
We are using the upgradeability proxy from zos
and the deployment pipeline is using their system as well. You can read more
about the publishing process and
upgrading in zos
documentation.
In order to interact with "real" networks such as mainnet
, ropsten
or others,
you need to setup a keys.json
file used by truffle
that does the heavy lifting for zos.
{
"mnemonic": "<SEED_PHRASE>",
"infura_projectid": "<PROJECT_ID>"
}
Upgradeability FAQ
What does upgradeability mean?
We can update the logic of Entrypoint, Segment Directory or Organization while keeping their
public address the same and without touching any data.
Can you change the Organization data structure?
The Organization Factory owner can, yes. As long as we adhere to
zos recommendations,
it should be safe. The same applies for Segment Directory, Entrypoint and Factory.
Can I switch to the new Organization version?
If you created your Organization via Organization Factory, no. The Organization Factory
owner has to do that for you. If you deployed the (upgradeable) Organization yourself or reclaimed the
proxy ownership from the Factory owner, you can do it yourself. If you used a non-upgradeable
smart contract implementation, then no.
Why do I keep getting "revert Cannot call fallback function from the proxy admin" when interacting with Organization?
This is a documented behaviour of zos upgradeability.
You need to call the proxied Organization contract from a different account than is the proxy owner.
What happens when you upgrade a Segment Directory?
The Directory address stays the same, the client software has to
interact with the Directory only with the updated ABI which is distributed
via NPM (under the new version number). No data is lost.
How do I work with different organization versions on the client?
That should be possible by using an ABI of OrganizationInterface
on the client side.
Local testing
You don't need keys.json
file for local testing of deployment and interaction
with the contracts.
- Start a local Ethereum network.
> npm run dev-net
- Start a zos session.
> ./node_modules/.bin/zos session --network development --from 0x87265a62c60247f862b9149423061b36b460f4BB --expires 3600
- Deploy your contracts. This only uploads the logic, the contracts are not meant to be directly
interacted with.
> ./node_modules/.bin/zos push --network development
- Create the proxy instances of deployed contracts you can interact with. The
args
attribute is passed to the initializer function. See documentation of the appropriate contracts
for details. The zos app might differ for each deployment. You don't need a deployed Lif token
to play with this locally.
> ./node_modules/.bin/zos create OrganizationFactory --network development --init initialize --args 0x87265a62c60247f862b9149423061b36b460f4BB,0x988f24d8356bf7e3D4645BA34068a5723BF3ec6B
> ./node_modules/.bin/zos create WindingTreeEntrypoint --network development --init initialize --args 0x87265a62c60247f862b9149423061b36b460f4BB,0xB6e225194a1C892770c43D4B529841C99b3DA1d7,0x4fC9beBEE86FdA621f48a4C6537807Bae59cc3e4
> ./node_modules/.bin/zos create SegmentDirectory --network development --init initialize --args 0x87265a62c60247f862b9149423061b36b460f4BB,hotels,0xB6e225194a1C892770c43D4B529841C99b3DA1d7
These commands will return a network address where you can actually interact with the contracts.
For a quick test, you can use the truffle console. We also need to use a different account than the
owner of the OrganizationFactory
to pose as the Organization
owner
> ./node_modules/.bin/truffle console --network development
truffle(development)> account = (await web3.eth.getAccounts())[1]
truffle(development)> entrypoint = await WindingTreeEntrypoint.at('0x08143AA1D053B0E563dC07B89a923caDb00644f1')
truffle(development)> entrypoint.setSegment('hotels', '0x9262d0b04d29a827777b77D6b997df4dC8A431ab')
truffle(development)> factory = await OrganizationFactory.at(await entrypoint.getOrganizationFactory({ from: account}))
truffle(development)> factory.create('https://windingtree.com', { from: account })
truffle(development)> factory.getCreatedOrganizations()
truffle(development)> directory = await SegmentDirectory.at(await entrypoint.getSegment('hotels', { from: account}))
truffle(development)> directory.getOrganizations()
truffle(development)> directory.add('0xa8c4cbB500da540D9fEd05BE7Bef0f0f5df3e2cc', { from: account })
truffle(development)> directory.getOrganizations()
[ '0x0000000000000000000000000000000000000000',
'0xa8c4cbB500da540D9fEd05BE7Bef0f0f5df3e2cc' ]
truffle(development)> organization = await OrganizationInterface.at('0xa8c4cbB500da540D9fEd05BE7Bef0f0f5df3e2cc')
truffle(development)> organization.getOrgJsonUri({ from: account })