├── .all-contributorsrc ├── .gitignore ├── ERCs ├── ERC20 │ ├── ERC20-TokenScript.xml │ ├── Makefile │ ├── README.md │ └── layouts │ │ ├── gaveApproval.en.js │ │ ├── item-view-approvalObtained.en.js │ │ ├── item-view-ownerApproved.en.js │ │ ├── item-view-received.en.js │ │ ├── item-view-sent.en.js │ │ ├── received.en.js │ │ ├── receivedApproval.en.js │ │ ├── sent.en.js │ │ └── shared.css ├── ERC721 │ ├── ERC721-TokenScript.xml │ ├── Makefile │ ├── README.md │ └── layouts │ │ ├── gaveApproval.en.js │ │ ├── item-view-approvalObtained.en.js │ │ ├── item-view-ownerApproved.en.js │ │ ├── item-view-received.en.js │ │ ├── item-view-sent.en.js │ │ ├── received.en.js │ │ ├── receivedApproval.en.js │ │ ├── sent.en.js │ │ └── shared.css └── README.md ├── LICENSE ├── README.md ├── bootstrap-js ├── .gitignore ├── BOOKY.xml ├── EntryToken.json ├── EntryToken.sol ├── EntryToken.xml ├── active-negotiation.html ├── active-negotiation_h.html ├── babel.config.js ├── blank.html ├── booky.json ├── inc │ ├── ethers-5.0.umd.min.js │ └── web3.min.js ├── modules │ └── ts_helpers.js ├── package.json ├── passive-negotiation.html ├── tests │ ├── helpers.test.js │ ├── modules.test.js-off │ └── tokenscript-bootstrap.test.js-off ├── tokenscript-bootstrap.js ├── ts.css └── ts.scss ├── doc ├── Makefile ├── The tech Stack.docx ├── articles │ ├── We need a better framework for applications using blockchain 中文 │ ├── data-object-ticket.svg │ └── data-object.md ├── attribute-processing.md ├── authenticator.dia ├── authenticator.svg ├── authenticity+trustworthiness.md ├── data+filter.md ├── getting_started.md ├── img │ ├── airbnb.jpeg │ ├── iconified-view.jpeg │ ├── readme │ │ ├── share_facebook-btn.svg │ │ ├── share_linkedin-btn.svg │ │ ├── share_mail-btn.svg │ │ ├── share_reddit-btn.svg │ │ ├── share_telegram-btn.svg │ │ ├── share_tweet-btn.svg │ │ └── tokenscript-stack.jpg │ ├── regular-view.jpeg │ ├── sovereign.svg │ └── token_data.svg ├── javascript_api.md ├── negotiator.dia ├── negotiator.svg └── short_paper.md ├── funding.json ├── schema ├── README.md ├── asnx.xsd ├── data.xsd ├── ethereum.xsd ├── tokenscript.xsd ├── xhtml1-strict.xsd ├── xml.xsd └── xmldsig-core-schema.xsd └── xmldsig ├── README.md └── js ├── README.md └── src ├── index.js ├── package.json ├── xmldom.patch └── xmldsigverifier.js /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "README.md" 4 | ], 5 | "imageSize": 100, 6 | "commit": false, 7 | "contributors": [ 8 | { 9 | "login": "colourful-land", 10 | "name": "Weiwu Zhang", 11 | "avatar_url": "https://avatars3.githubusercontent.com/u/548435?v=4", 12 | "profile": "https://github.com/colourful-land", 13 | "contributions": [ 14 | "code" 15 | ] 16 | }, 17 | { 18 | "login": "James-Sangalli", 19 | "name": "James Sangalli", 20 | "avatar_url": "https://avatars2.githubusercontent.com/u/16630514?v=4", 21 | "profile": "https://github.com/James-Sangalli", 22 | "contributions": [ 23 | "code" 24 | ] 25 | }, 26 | { 27 | "login": "theBergmann", 28 | "name": "theBergmann", 29 | "avatar_url": "https://avatars1.githubusercontent.com/u/25482130?s=400&v=4", 30 | "profile": "https://github.com/theBergmann", 31 | "contributions": [ 32 | "code" 33 | ] 34 | }, 35 | { 36 | "login": "hboon", 37 | "name": "Hwee-Boon Yar", 38 | "avatar_url": "https://avatars1.githubusercontent.com/u/56189?v=4", 39 | "profile": "https://github.com/hboon", 40 | "contributions": [ 41 | "code" 42 | ] 43 | }, 44 | { 45 | "login": "zhangzhongnan928", 46 | "name": "Victor Zhang", 47 | "avatar_url": "https://avatars0.githubusercontent.com/u/33795543?v=4", 48 | "profile": "https://github.com/zhangzhongnan928", 49 | "contributions": [ 50 | "code" 51 | ] 52 | }, 53 | { 54 | "login": "liuxiaohao", 55 | "name": "maxliu", 56 | "avatar_url": "https://avatars0.githubusercontent.com/u/1217967?s=400&u=d09aff7ab31b53ffffb2af8bd8d41eda7e3b79fe&v=4", 57 | "profile": "https://github.com/liuxiaohao", 58 | "contributions": [ 59 | "code" 60 | ] 61 | }, 62 | { 63 | "login": "JamesSmartCell", 64 | "name": "James Brown", 65 | "avatar_url": "https://avatars2.githubusercontent.com/u/12689544?v=4", 66 | "profile": "https://github.com/JamesSmartCell", 67 | "contributions": [ 68 | "code" 69 | ] 70 | }, 71 | { 72 | "login": "developerpeachy", 73 | "name": "Rosalie", 74 | "avatar_url": "https://avatars3.githubusercontent.com/u/13824586?s=400&u=329f22d53d8c50f3877f909a6a7f0321d1e215db&v=4", 75 | "profile": "https://github.com/developerpeachy", 76 | "contributions": [ 77 | "code" 78 | ] 79 | }, 80 | { 81 | "login": "jzaki", 82 | "name": "James Zaki", 83 | "avatar_url": "https://avatars3.githubusercontent.com/u/939603?v=4", 84 | "profile": "https://github.com/jzaki", 85 | "contributions": [ 86 | "code" 87 | ] 88 | }, 89 | { 90 | "login": "farrahfazirah", 91 | "name": "Farrah Fazirah", 92 | "avatar_url": "https://avatars2.githubusercontent.com/u/20555752?s=460&u=74320573120d8411594a3ffa48e2c6a1a5be3257&v=4", 93 | "profile": "https://github.com/farrahfazirah", 94 | "contributions": [ 95 | "code" 96 | ] 97 | }, 98 | { 99 | "login": "lanlan3322", 100 | "name": "Laurence", 101 | "avatar_url": "https://avatars0.githubusercontent.com/u/26592081?s=400&u=e70d78508c13db2b533ac081c3677b9aea85c8cf&v=4", 102 | "profile": "https://github.com/lanlan3322", 103 | "contributions": [ 104 | "code" 105 | ] 106 | }, 107 | { 108 | "login": "hellolucas", 109 | "name": "Lucas Toledo", 110 | "avatar_url": "https://avatars3.githubusercontent.com/u/17125002?v=4", 111 | "profile": "https://github.com/hellolucas", 112 | "contributions": [ 113 | "code" 114 | ] 115 | } 116 | ], 117 | "contributorsPerLine": 7, 118 | "projectName": "tokenscript", 119 | "projectOwner": "AlphaWallet", 120 | "repoType": "github", 121 | "repoHost": "https://github.com", 122 | "skipCi": true 123 | } 124 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pdf 2 | .DS_Store 3 | .idea 4 | *.map 5 | *.canonicalized.xml 6 | !resources/fast-token-onboarding/*.canonicalized.xml 7 | *.canonicalized.xml.INVALID 8 | *.tsml 9 | *.p12 10 | !shong.wang.p12 11 | .eslintcache 12 | -------------------------------------------------------------------------------- /ERCs/ERC20/ERC20-TokenScript.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | ]> 13 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 1.3.6.1.4.1.1466.115.121.1.26 100 | 101 | 102 | name 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 1.3.6.1.4.1.1466.115.121.1.36 112 | 113 | 114 | balance 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 1.3.6.1.4.1.1466.115.121.1.36 128 | 129 | 130 | decimals 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 1.3.6.1.4.1.1466.115.121.1.26 140 | 141 | 142 | symbol 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /ERCs/ERC20/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(TOKENSCRIPT_SCHEMA),) 2 | TOKENSCRIPT_SCHEMA=http://tokenscript.org/2020/06/tokenscript.xsd 3 | endif 4 | 5 | ifeq ($(XMLSECTOOL),) 6 | XMLSECTOOL=xmlsectool 7 | endif 8 | 9 | ifeq ($(XMLLINT),) 10 | XMLLINT=xmllint 11 | endif 12 | 13 | ifeq ($(XMLSEC),) 14 | XMLSEC=xmlsec1 # xmlsec for Linux/Windows 15 | endif 16 | 17 | 18 | ifndef KEY 19 | KEY=1 20 | endif 21 | 22 | SIGNATURE_ALGORITHM=rsa-sha256 23 | 24 | help: 25 | # Needs a target, example: $$ make EntryToken.canonicalized.xml 26 | # 27 | # Let's say you have a TokenScript "EntryToken.xml" 28 | # - to validate and canonicalize, add 'canonicalized' in the filename 29 | @echo $$ make EntryToken.canonicalized.xml 30 | # - to sign, use tsml as file extension: 31 | @echo $$ make EntryToken.tsml 32 | 33 | %.canonicalized.xml : %.xml 34 | # XML canonicalization and validation against TS schema 35 | $(XMLLINT) --c14n $^ > $@ && \ 36 | $(XMLLINT) --noout --schema $(TOKENSCRIPT_SCHEMA) $@ || \ 37 | (mv $@ $@.TEST && exit 1) 38 | 39 | %.tsml: %.canonicalized.xml 40 | ifeq (,$(KEYPASSWORD)) 41 | $(error KEYPASSWORD is not set) 42 | endif 43 | ifeq (,$(KEYSTORE)) 44 | @echo ---------------- Keystore missing. Try this ---------------- 45 | @echo $$ make KEYSTORE=shong.wang.p12 KEYPASSWORD=shong.wang KEYINFO='"Shong Wang"' $@ 46 | @echo replace it with your .p12 file and your password 47 | rm $^ 48 | else 49 | # Signing with xmlsec requires original .xml file to contain the Signature tag. 50 | # $(XMLSEC) sign --pkcs12:"$(KEYINFO)" $(KEYSTORE) --pwd "$(KEYPASSWORD)" --output $@ $^ 51 | # For now use xmlsectool... 52 | $(XMLSECTOOL) --sign --keyInfoKeyName "$(KEYINFO)" --digest SHA-256 --signatureAlgorithm http://www.w3.org/2001/04/xmldsig-more#$(SIGNATURE_ALGORITHM) --inFile $^ --outFile $@ --keystore $(KEYSTORE) --keystoreType PKCS12 --key $(KEY) --keyPassword "$(KEYPASSWORD)" --signaturePosition LAST 53 | # removing the canonicalized created for validation 54 | rm $^ 55 | endif 56 | -------------------------------------------------------------------------------- /ERCs/ERC20/README.md: -------------------------------------------------------------------------------- 1 | ## Congratulations on generating a TokenScript for your token! 2 | 3 | ### For more information 4 | Check out the tutorial here: https://github.com/AlphaWallet/TokenScript-Examples/tree/master/tutorial, it includes comprehensive information on what a TokenScript is, how to use it on AlphaWallet and AlphaWallet forks, what tools you need to debug and more! 5 | 6 | ### Happy with your TokenScript and wanna show off your token to everyone on AlphaWallet? 7 | Submit a pull request with your token added to https://github.com/AlphaWallet/TokenScript-Examples 8 | 9 | ### Learn More about AlphaWallet 10 | iOS download: https://apps.apple.com/us/app/alphawallet/id1358230430 11 | 12 | Android download: https://play.google.com/store/apps/details?id=io.stormbird.wallet&hl=en_AU 13 | 14 | Website: AlphaWallet.com 15 | 16 | GitHub: https://github.com/AlphaWallet/ 17 | 18 | Forum: https://community.tokenscript.org/ 19 | 20 | Twitter: @AlphaWallet 21 | 22 | Telegram: t.me/AlphaWalletGroup 23 | 24 | Facebook: https://www.facebook.com/AlphaWallet/ 25 | -------------------------------------------------------------------------------- /ERCs/ERC20/layouts/shared.css: -------------------------------------------------------------------------------- 1 | h3 { color: #111; font-family: 'Open Sans', sans-serif; font-size: 20px; font-weight: 300; line-height: 32px; } 2 | 3 | #inputBox { 4 | text-align: center; 5 | } 6 | 7 | html, 8 | body { 9 | height: 100%; 10 | } 11 | html { 12 | font-size: 14px; 13 | } 14 | body { 15 | margin: 0px; 16 | padding: 0px; 17 | overflow-x: hidden; 18 | min-width: 320px; 19 | background: #FFFFFF; 20 | font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif; 21 | font-size: 14px; 22 | line-height: 1.4285em; 23 | color: rgba(0, 0, 0, 0.87); 24 | font-smoothing: antialiased; 25 | } 26 | .ui.container { 27 | display: block; 28 | max-width: 100% !important; 29 | } 30 | @media only screen and (max-width: 767px) { 31 | .ui.container { 32 | width: auto !important; 33 | margin-left: 1em !important; 34 | margin-right: 1em !important; 35 | } 36 | } 37 | @media only screen and (min-width: 768px) and (max-width: 991px) { 38 | .ui.container { 39 | width: 723px; 40 | margin-left: auto !important; 41 | margin-right: auto !important; 42 | } 43 | } 44 | @media only screen and (min-width: 992px) and (max-width: 1199px) { 45 | .ui.container { 46 | width: 933px; 47 | margin-left: auto !important; 48 | margin-right: auto !important; 49 | } 50 | } 51 | @media only screen and (min-width: 1200px) { 52 | .ui.container { 53 | width: 1127px; 54 | margin-left: auto !important; 55 | margin-right: auto !important; 56 | } 57 | } 58 | .ui.segment { 59 | position: relative; 60 | background: #FFFFFF; 61 | -webkit-box-shadow: 0px 1px 2px 0 rgba(34, 36, 38, 0.15); 62 | box-shadow: 0px 1px 2px 0 rgba(34, 36, 38, 0.15); 63 | margin: 0.5rem 0em; 64 | padding: 0.5em 0.5em; 65 | border-radius: 0.28571429rem; 66 | border: 1px solid rgba(34, 36, 38, 0.15); 67 | text-align: center; 68 | } 69 | .ui.segment:first-child { 70 | margin-top: 0em; 71 | } 72 | .ui.segment:last-child { 73 | margin-bottom: 0em; 74 | } 75 | input { 76 | position: relative; 77 | font-weight: normal; 78 | font-style: normal; 79 | font-size: 12px; 80 | display: -ms-inline-flexbox; 81 | display: inline-flex; 82 | color: rgba(0, 0, 0, 0.87); 83 | padding: 9.5px 14px; 84 | width: 300px; 85 | border-color: #D8D8D8; 86 | } 87 | input[type=text]:focus { 88 | border-color: #D8D8D8; 89 | background: #FAFAFA; 90 | color: rgba(0, 0, 0, 0.87); 91 | -webkit-box-shadow: none; 92 | box-shadow: none; 93 | } 94 | label { 95 | font-size: 12px; 96 | font-weight: 500; 97 | margin-top: 6px; 98 | } -------------------------------------------------------------------------------- /ERCs/ERC721/ERC721-TokenScript.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | ]> 13 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 1.3.6.1.4.1.1466.115.121.1.26 105 | 106 | 107 | name 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 1.3.6.1.4.1.1466.115.121.1.26 117 | 118 | 119 | symbol 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /ERCs/ERC721/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(TOKENSCRIPT_SCHEMA),) 2 | TOKENSCRIPT_SCHEMA=http://tokenscript.org/2020/06/tokenscript.xsd 3 | endif 4 | 5 | ifeq ($(XMLSECTOOL),) 6 | XMLSECTOOL=xmlsectool 7 | endif 8 | 9 | ifeq ($(XMLLINT),) 10 | XMLLINT=xmllint 11 | endif 12 | 13 | ifeq ($(XMLSEC),) 14 | XMLSEC=xmlsec1 # xmlsec for Linux/Windows 15 | endif 16 | 17 | 18 | ifndef KEY 19 | KEY=1 20 | endif 21 | 22 | SIGNATURE_ALGORITHM=rsa-sha256 23 | 24 | help: 25 | # Needs a target, example: $$ make EntryToken.canonicalized.xml 26 | # 27 | # Let's say you have a TokenScript "EntryToken.xml" 28 | # - to validate and canonicalize, add 'canonicalized' in the filename 29 | @echo $$ make EntryToken.canonicalized.xml 30 | # - to sign, use tsml as file extension: 31 | @echo $$ make EntryToken.tsml 32 | 33 | %.canonicalized.xml : %.xml 34 | # XML canonicalization and validation against TS schema 35 | $(XMLLINT) --c14n $^ > $@ && \ 36 | $(XMLLINT) --noout --schema $(TOKENSCRIPT_SCHEMA) $@ || \ 37 | (mv $@ $@.TEST && exit 1) 38 | 39 | %.tsml: %.canonicalized.xml 40 | ifeq (,$(KEYPASSWORD)) 41 | $(error KEYPASSWORD is not set) 42 | endif 43 | ifeq (,$(KEYSTORE)) 44 | @echo ---------------- Keystore missing. Try this ---------------- 45 | @echo $$ make KEYSTORE=shong.wang.p12 KEYPASSWORD=shong.wang KEYINFO='"Shong Wang"' $@ 46 | @echo replace it with your .p12 file and your password 47 | rm $^ 48 | else 49 | # Signing with xmlsec requires original .xml file to contain the Signature tag. 50 | # $(XMLSEC) sign --pkcs12:"$(KEYINFO)" $(KEYSTORE) --pwd "$(KEYPASSWORD)" --output $@ $^ 51 | # For now use xmlsectool... 52 | $(XMLSECTOOL) --sign --keyInfoKeyName "$(KEYINFO)" --digest SHA-256 --signatureAlgorithm http://www.w3.org/2001/04/xmldsig-more#$(SIGNATURE_ALGORITHM) --inFile $^ --outFile $@ --keystore $(KEYSTORE) --keystoreType PKCS12 --key $(KEY) --keyPassword "$(KEYPASSWORD)" --signaturePosition LAST 53 | # removing the canonicalized created for validation 54 | rm $^ 55 | endif 56 | -------------------------------------------------------------------------------- /ERCs/ERC721/README.md: -------------------------------------------------------------------------------- 1 | ## Congratulations on generating a TokenScript for your token! 2 | 3 | ### For more information 4 | Check out the tutorial here: https://github.com/AlphaWallet/TokenScript-Examples/tree/master/tutorial, it includes comprehensive information on what a TokenScript is, how to use it on AlphaWallet and AlphaWallet forks, what tools you need to debug and more! 5 | 6 | ### Happy with your TokenScript and wanna show off your token to everyone on AlphaWallet? 7 | Submit a pull request with your token added to https://github.com/AlphaWallet/TokenScript-Examples 8 | 9 | ### Learn More about AlphaWallet 10 | iOS download: https://apps.apple.com/us/app/alphawallet/id1358230430 11 | 12 | Android download: https://play.google.com/store/apps/details?id=io.stormbird.wallet&hl=en_AU 13 | 14 | Website: AlphaWallet.com 15 | 16 | GitHub: https://github.com/AlphaWallet/ 17 | 18 | Forum: https://community.tokenscript.org/ 19 | 20 | Twitter: @AlphaWallet 21 | 22 | Telegram: t.me/AlphaWalletGroup 23 | 24 | Facebook: https://www.facebook.com/AlphaWallet/ 25 | -------------------------------------------------------------------------------- /ERCs/ERC721/layouts/shared.css: -------------------------------------------------------------------------------- 1 | h3 { color: #111; font-family: 'Open Sans', sans-serif; font-size: 20px; font-weight: 300; line-height: 32px; } 2 | 3 | #inputBox { 4 | text-align: center; 5 | } 6 | 7 | html, 8 | body { 9 | height: 100%; 10 | } 11 | html { 12 | font-size: 14px; 13 | } 14 | body { 15 | margin: 0px; 16 | padding: 0px; 17 | overflow-x: hidden; 18 | min-width: 320px; 19 | background: #FFFFFF; 20 | font-family: 'Lato', 'Helvetica Neue', Arial, Helvetica, sans-serif; 21 | font-size: 14px; 22 | line-height: 1.4285em; 23 | color: rgba(0, 0, 0, 0.87); 24 | font-smoothing: antialiased; 25 | } 26 | .ui.container { 27 | display: block; 28 | max-width: 100% !important; 29 | } 30 | @media only screen and (max-width: 767px) { 31 | .ui.container { 32 | width: auto !important; 33 | margin-left: 1em !important; 34 | margin-right: 1em !important; 35 | } 36 | } 37 | @media only screen and (min-width: 768px) and (max-width: 991px) { 38 | .ui.container { 39 | width: 723px; 40 | margin-left: auto !important; 41 | margin-right: auto !important; 42 | } 43 | } 44 | @media only screen and (min-width: 992px) and (max-width: 1199px) { 45 | .ui.container { 46 | width: 933px; 47 | margin-left: auto !important; 48 | margin-right: auto !important; 49 | } 50 | } 51 | @media only screen and (min-width: 1200px) { 52 | .ui.container { 53 | width: 1127px; 54 | margin-left: auto !important; 55 | margin-right: auto !important; 56 | } 57 | } 58 | .ui.segment { 59 | position: relative; 60 | background: #FFFFFF; 61 | -webkit-box-shadow: 0px 1px 2px 0 rgba(34, 36, 38, 0.15); 62 | box-shadow: 0px 1px 2px 0 rgba(34, 36, 38, 0.15); 63 | margin: 0.5rem 0em; 64 | padding: 0.5em 0.5em; 65 | border-radius: 0.28571429rem; 66 | border: 1px solid rgba(34, 36, 38, 0.15); 67 | text-align: center; 68 | } 69 | .ui.segment:first-child { 70 | margin-top: 0em; 71 | } 72 | .ui.segment:last-child { 73 | margin-bottom: 0em; 74 | } 75 | input { 76 | position: relative; 77 | font-weight: normal; 78 | font-style: normal; 79 | font-size: 12px; 80 | display: -ms-inline-flexbox; 81 | display: inline-flex; 82 | color: rgba(0, 0, 0, 0.87); 83 | padding: 9.5px 14px; 84 | width: 300px; 85 | border-color: #D8D8D8; 86 | } 87 | input[type=text]:focus { 88 | border-color: #D8D8D8; 89 | background: #FAFAFA; 90 | color: rgba(0, 0, 0, 0.87); 91 | -webkit-box-shadow: none; 92 | box-shadow: none; 93 | } 94 | label { 95 | font-size: 12px; 96 | font-weight: 500; 97 | margin-top: 6px; 98 | } -------------------------------------------------------------------------------- /ERCs/README.md: -------------------------------------------------------------------------------- 1 | # ERCs implemented in TokenScript 2 | 3 | This directory has a few prominent ERCs implemented in 4 | TokenScript. Unlike a typical tokenscript, these TokenScripts do not 5 | have smart contracts defined in them, and each token's tokenscript can 6 | choose to override the named cards and named attributes defined in 7 | these TokenScripts. 8 | 9 | ## Progress 10 | 11 | As of Jun 2020, these ERC implementations have the token attributes defined in 12 | each ERCs and activity cards, but no token cards or action cards. These will be 13 | amended in the coming months. 14 | 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 AlphaWallet 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # TokenScript: Add Rich Functionality To Your Tokens 3 | | Web Era | New Digital Primitives | 4 | |---------|-------------------| 5 | | Web1 [R] | • Website | 6 | | Web2 [R,W] | • Web App
• Mobile App | 7 | | Web3 [R,W,O] | • DApp [R,W]
• Token [O]
• Interactive Token [R,W,O]*
| 8 | 9 | [R] = Read [W] = Write [O] = Own 10 | 11 | *Interactive Token example: Smart Token (ERC5169 + TokenScript) 12 | 13 | **Bring context, security and cross-platform functionality (iOS, Android and Web) to your favourite tokens with a single file.** 14 | 15 | TokenScript makes Smart Token (Credit to [Virgil Griffith](https://twitter.com/virgilgr) for coming up with the term). These are like traditional ERC20 or ERC721 tokens, but with extendable structure & signed JavaScript to realise rich functions and full composability that DApps struggle to implement, and be traded with flexible, customisable trading rules. 16 | 17 | A TokenScript file is made of 18 | - JavaScript to make Token work in the user's wallet or across multiple apps; and 19 | - XML data to extract status and value of the token. 20 | 21 | In short, it's like a secure front-end for tokens. 22 | 23 | **Benefits** 24 | - Run your tokens from users wallets as native, modular ‘Mini-DApps' 25 | - Extend token structure and realise rich functions with a single file 26 | - Portable across DApps 27 | - Sync updates at any time 28 | - Blockchain agnostic 29 | - Secure Enclave 30 | - DvP Security 31 | - Context based programming: User-experience 32 | - Attestation 33 | 34 | ![tokenscript stack alphawallet dapps](/doc/img/readme/tokenscript-stack.jpg) 35 | 36 | ## Where can I find examples of already complete TokenScripts that I can use to create my own? 37 | Visit our example Repo [here](https://github.com/AlphaWallet/TokenScript-Examples), which is full of complete TokenScripts that run inside our wallet, AlphaWallet. 38 | 39 | **Working examples:** 40 | - [Bartercard Qoin](https://play.google.com/store/apps/details?id=com.qoin.wallet&hl=en) 41 | - [FIFA and UEFA’s blockchain tickets](https://apps.apple.com/au/app/shankai/id1492559481) 42 | - [Car Ownership portal](https://github.com/AlphaWallet/TokenScript-Examples/tree/master/examples/Karma) 43 | 44 | [**A really good starting point to generating your own TokenScript**](https://github.com/AlphaWallet/TokenScript-Examples/tree/master/tutorial#future-obtaining-sample-files-schema-202003) 45 | 46 | ## How is TokenScript created and used? 47 | 48 | A TokenScript is typically created by the Token's issuer — the team which builds the underlying smart contracts dictating the token's transaction rules. 49 | 50 | When used by a **User** through a user agent (such as a DApp browser), TokenScript visually renders the token and provides trustworthy assembling of transactions related to the token. 51 | 52 | When used by a **DApp developer**, TokenScript allows a DApp to interact with its interface instead of directly accessing smart contracts, so that the DApp can be upgraded independently of all the tokens it supports. 53 | 54 | When used by a **Market** or any other token related service (e.g. an auction or collateralisation), it allows the token to be correctly rendered and signed for use with these services. 55 | 56 | ## What's in a TokenScript file? 57 | 58 | TokenScript is an XML dialect. It describes: 59 | 60 | - The functions provided by the token (through smart contract or not) 61 | - The method to render it on the user's interface 62 | - The ERCs token behaviour templates it uses 63 | - The JavaScript needed to construct transactions and render the token. 64 | 65 | It also defines how attestations are used to decorate, or convert to, or validate a transaction. 66 | 67 | ## Why TokenScript? 68 | 69 | Today, the way tokens are accessed, rendered and transacted are scattered across DApps and Smart Contracts. This limited the use of Tokens. 70 | 71 | Typically, all knowledge about rendering a token and constructing a transaction about the token is in a "host" web app. The "host" web app becomes a centre in the token's marketisation and integration, recreating data interoperability, security and availability barrier - precisely the same set of issues that prevented tokenisation before blockchain's invention. 72 | 73 | By taking the knowledge of tokens including smart contract interfaces out and put them into a portable TokenScript we allow tokens to be accessible and useful. 74 | 75 | The team at Smart Layer is committed to bringing Web 3.0 via tokenization. Tokenised rights can be traded on the market and integrated across systems, forming a [Frictionless Market](https://github.com/AlphaWallet/TokenScript/blob/master/doc/design_paper.md#creating-a-frictionless-market) and allowing [Limitless Integration](https://github.com/AlphaWallet/TokenScript/blob/master/doc/design_paper.md#blockchain-integrates-the-web). 76 | 77 | ## Why use XML rather than JSON or some other JS format? 78 | 79 | It is helpful to think of the TokenScript file as a project file and the canonicalized version as the final distributable, build target. 80 | 81 | XML has certain standards and tools that have been built over time that helps us here: 82 | 83 | 1. XML canonicalization (c14n) which specifies and provides a portable way to represent an XML file under transmission in an always identical format. 84 | 85 | 2. XML digital Signatures which is based on signing canonicalized XMLs. 86 | 87 | 3. XML allows developers to list and describe attributes and actions/transactions declaratively. While it's possible to do it with JSON, it would likely involve listing them in dictionary and string literals that are harder to enforce schema, validation and track schema changes. 88 | 89 | 4. standardized static types, with XML we can easily enforce ASN.1 variable encodings to ensure the variable is as defined. 90 | 91 | Used together, we can ensure that a given signed, canonicalized TokenScript file has not been tampered with. Without using XML, these crucial properties of XML have to be reinvented and made available. 92 | 93 | Ultimately, if we look at the TokenScript XML file as a project file, we can foresee that in the future, we might build tools to manage them instead of relying on editing the XML file directly, then how the file is itself being editable ceases to be that important and integrity of the file becomes more important. 94 | 95 | ## TokenScript Project introduction 96 | 97 | The TokenScript project is an initiative to design, progress TokenScript development and incentivise use. 98 | 99 | ## Git repo 100 | 101 | This project holds: 102 | `doc` : documents about the language and the design 103 | `schema` : XML schema files which define the syntax for TokenScripts 104 | 105 | 106 | ## Join the conversation or contribute 107 | 108 | Looking for support, have feedback or questions? We'd love to hear your thoughts and see what you come up with. 109 | 110 | Please join the conversation at [Telegram](https://t.me/AlphaWalletGroup), [Twitter](https://twitter.com/AlphaWallet) or through our [community forums](https://community.tokenscript.org/). 111 | 112 | **Donation Address** 113 | ETH: [0xdeadd42a3ab7d14626a98eadebd26ae8c81b07e4](https://etherscan.io/address/0xdeadd42a3ab7d14626a98eadebd26ae8c81b07e4) 114 | 115 | Want to support the TokenScript team? A cup of coffee or beer is always appreciated! ☕ 116 | 117 | ## Contributors 118 | 119 | Thank you to all the contributors! You are awesome. 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 |

Weiwu Zhang

💻

James Sangalli

💻

theBergmann

💻

Hwee-Boon Yar

💻

Victor Zhang

💻

maxliu

💻

James Brown

💻

Rosalie

💻

James Zaki

💻

Farrah Fazirah

💻

Laurence

💻

Lucas Toledo

💻
142 | 143 | 144 | 145 | 146 | 147 | ## License 148 | TokenScript is available under the [MIT license](https://github.com/AlphaWallet/TokenScript/blob/master/LICENSE). Free for commercial and non-commercial use. 149 | -------------------------------------------------------------------------------- /bootstrap-js/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .sass-cache/ 3 | package-lock.json 4 | *.map 5 | *.zip -------------------------------------------------------------------------------- /bootstrap-js/active-negotiation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | Showing the cards in a web page 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 198 | 199 | 200 |

Demonstration of showing a card in a web page

201 |

Token Card

202 |
203 |

204 |

206 | 207 |
208 |
209 | 210 | 211 | 212 |
213 |
Uploading…
214 |
Done! Upload more?
215 |
Error! . Try again!
216 |
217 | 218 |
219 |

You can add remote token XML file URL here

220 | 221 |
Download remote token
222 |
223 | 224 |
225 |

You can add token filter string here

226 | 227 |
Renegotiate All tokens with current filter
228 |
229 | 230 | 231 | 232 | -------------------------------------------------------------------------------- /bootstrap-js/active-negotiation_h.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | Showing the cards in a web page 11 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 198 | 199 | 200 |

Demonstration of showing a card in a web page

201 |

Token Card

202 |
203 |

204 |

206 | 207 |
208 |
209 | 210 | 211 | 212 |
213 |
Uploading…
214 |
Done! Upload more?
215 |
Error! . Try again!
216 |
217 | 218 |
219 |

You can add remote token XML file URL here

220 | 221 |
Download remote token
222 |
223 | 224 |
225 |

You can add token filter string here

226 | 227 |
Renegotiate All tokens with current filter
228 |
229 | 230 | 231 | 232 | -------------------------------------------------------------------------------- /bootstrap-js/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | targets: { 7 | node: 'current', 8 | }, 9 | }, 10 | ], 11 | ], 12 | }; -------------------------------------------------------------------------------- /bootstrap-js/blank.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /bootstrap-js/booky.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "name", 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "string" 10 | } 11 | ], 12 | "payable": false, 13 | "type": "function" 14 | }, 15 | { 16 | "constant": false, 17 | "inputs": [ 18 | { 19 | "name": "_spender", 20 | "type": "address" 21 | }, 22 | { 23 | "name": "_amount", 24 | "type": "uint256" 25 | } 26 | ], 27 | "name": "approve", 28 | "outputs": [ 29 | { 30 | "name": "success", 31 | "type": "bool" 32 | } 33 | ], 34 | "payable": false, 35 | "type": "function" 36 | }, 37 | { 38 | "constant": true, 39 | "inputs": [], 40 | "name": "totalSupply", 41 | "outputs": [ 42 | { 43 | "name": "totalSupply", 44 | "type": "uint256" 45 | } 46 | ], 47 | "payable": false, 48 | "type": "function" 49 | }, 50 | { 51 | "constant": false, 52 | "inputs": [ 53 | { 54 | "name": "_from", 55 | "type": "address" 56 | }, 57 | { 58 | "name": "_to", 59 | "type": "address" 60 | }, 61 | { 62 | "name": "_amount", 63 | "type": "uint256" 64 | } 65 | ], 66 | "name": "transferFrom", 67 | "outputs": [ 68 | { 69 | "name": "success", 70 | "type": "bool" 71 | } 72 | ], 73 | "payable": false, 74 | "type": "function" 75 | }, 76 | { 77 | "constant": true, 78 | "inputs": [], 79 | "name": "decimals", 80 | "outputs": [ 81 | { 82 | "name": "", 83 | "type": "uint8" 84 | } 85 | ], 86 | "payable": false, 87 | "type": "function" 88 | }, 89 | { 90 | "constant": true, 91 | "inputs": [ 92 | { 93 | "name": "_owner", 94 | "type": "address" 95 | } 96 | ], 97 | "name": "balanceOf", 98 | "outputs": [ 99 | { 100 | "name": "balance", 101 | "type": "uint256" 102 | } 103 | ], 104 | "payable": false, 105 | "type": "function" 106 | }, 107 | { 108 | "constant": true, 109 | "inputs": [], 110 | "name": "owner", 111 | "outputs": [ 112 | { 113 | "name": "", 114 | "type": "address" 115 | } 116 | ], 117 | "payable": false, 118 | "type": "function" 119 | }, 120 | { 121 | "constant": true, 122 | "inputs": [], 123 | "name": "symbol", 124 | "outputs": [ 125 | { 126 | "name": "", 127 | "type": "string" 128 | } 129 | ], 130 | "payable": false, 131 | "type": "function" 132 | }, 133 | { 134 | "constant": false, 135 | "inputs": [ 136 | { 137 | "name": "_to", 138 | "type": "address" 139 | }, 140 | { 141 | "name": "_amount", 142 | "type": "uint256" 143 | } 144 | ], 145 | "name": "transfer", 146 | "outputs": [ 147 | { 148 | "name": "success", 149 | "type": "bool" 150 | } 151 | ], 152 | "payable": false, 153 | "type": "function" 154 | }, 155 | { 156 | "constant": true, 157 | "inputs": [ 158 | { 159 | "name": "_owner", 160 | "type": "address" 161 | }, 162 | { 163 | "name": "_spender", 164 | "type": "address" 165 | } 166 | ], 167 | "name": "allowance", 168 | "outputs": [ 169 | { 170 | "name": "remaining", 171 | "type": "uint256" 172 | } 173 | ], 174 | "payable": false, 175 | "type": "function" 176 | }, 177 | { 178 | "constant": false, 179 | "inputs": [ 180 | { 181 | "name": "ethers", 182 | "type": "uint256" 183 | } 184 | ], 185 | "name": "withdrawEthers", 186 | "outputs": [ 187 | { 188 | "name": "ok", 189 | "type": "bool" 190 | } 191 | ], 192 | "payable": false, 193 | "type": "function" 194 | }, 195 | { 196 | "inputs": [ 197 | { 198 | "name": "_name", 199 | "type": "string" 200 | }, 201 | { 202 | "name": "_symbol", 203 | "type": "string" 204 | }, 205 | { 206 | "name": "_decimals", 207 | "type": "uint8" 208 | } 209 | ], 210 | "payable": false, 211 | "type": "constructor" 212 | }, 213 | { 214 | "payable": true, 215 | "type": "fallback" 216 | }, 217 | { 218 | "anonymous": false, 219 | "inputs": [ 220 | { 221 | "indexed": true, 222 | "name": "_owner", 223 | "type": "address" 224 | }, 225 | { 226 | "indexed": false, 227 | "name": "_amount", 228 | "type": "uint256" 229 | } 230 | ], 231 | "name": "TokensCreated", 232 | "type": "event" 233 | }, 234 | { 235 | "anonymous": false, 236 | "inputs": [ 237 | { 238 | "indexed": true, 239 | "name": "_from", 240 | "type": "address" 241 | }, 242 | { 243 | "indexed": true, 244 | "name": "_to", 245 | "type": "address" 246 | }, 247 | { 248 | "indexed": false, 249 | "name": "_value", 250 | "type": "uint256" 251 | } 252 | ], 253 | "name": "Transfer", 254 | "type": "event" 255 | }, 256 | { 257 | "anonymous": false, 258 | "inputs": [ 259 | { 260 | "indexed": true, 261 | "name": "_owner", 262 | "type": "address" 263 | }, 264 | { 265 | "indexed": true, 266 | "name": "_spender", 267 | "type": "address" 268 | }, 269 | { 270 | "indexed": false, 271 | "name": "_value", 272 | "type": "uint256" 273 | } 274 | ], 275 | "name": "Approval", 276 | "type": "event" 277 | } 278 | ] -------------------------------------------------------------------------------- /bootstrap-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bootstrap-ts", 3 | "version": "1.0.0", 4 | "description": "Display TS token Views in the Web browser", 5 | "main": "index.js", 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "@babel/core": "^7.11.6", 9 | "@babel/preset-env": "^7.11.5", 10 | "babel-jest": "^26.5.2", 11 | "jest": "^26.5.2" 12 | }, 13 | "scripts": { 14 | "test": "jest" 15 | }, 16 | "jest": { 17 | "collectCoverage": true 18 | }, 19 | "author": "Oleh Hryb", 20 | "license": "ISC" 21 | } 22 | -------------------------------------------------------------------------------- /bootstrap-js/passive-negotiation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | Showing the cards in a web page 18 | 19 | 20 | 21 | 22 | 67 | 68 | 69 |

Demonstration of showing a card in a web page

70 |

Token Card will be in the top right corner of the screen

71 | 72 |

73 |

74 | 75 | 76 | -------------------------------------------------------------------------------- /bootstrap-js/tests/helpers.test.js: -------------------------------------------------------------------------------- 1 | let ts = require("../modules/ts_helpers.js"); 2 | 3 | test("Returns string query compared to object", () => { 4 | let str1 = "(&(objectClass=Person)(!(price<=4444))(|(locality=Jensen)(locality=Sydney)(locality=Babs J*)((locality=Sy*))))"; 5 | let str2 = "(&(!(price<=4444))(|(locality=Jensen)(locality=Sydney)(locality=Babs J*)((locality=Sy*))))"; 6 | let str3 = "(ownerBalance=)"; 7 | let props = { 8 | locality: 'Sydney', 9 | state: 'NSW', 10 | price: 15559 11 | } 12 | expect(ts.compareStringToProps(str1, props)).toBe(0); 13 | expect(ts.compareStringToProps(str2, props)).toBe(1); 14 | expect(ts.compareStringToProps(str3, props)).toBe(0); 15 | expect(ts.compareStringToProps(" ", props)).toBe(1); 16 | }); 17 | 18 | /** 19 | * @jest-environment jsdom 20 | */ 21 | test("test getContractAddress", () => { 22 | 23 | 24 | let tokenXMLText = '\n' + 25 | '' + 31 | ' \n' + 32 | ' 0x63cCEF733a093E5Bd773b41C96D3eCE361464942\n' + 33 | ' 0xFB82A5a2922A249f32222316b9D1F5cbD3838678\n' + 34 | ' 0x59a7a9fd49fabd07c0f8566ae4be96fcf20be5e1\n' + 35 | ' 0x2B58A9403396463404c2e397DBF37c5EcCAb43e5\n' + 36 | ' ' + 37 | ''; 38 | 39 | var parser = new DOMParser(); 40 | let xmlDoc = parser.parseFromString(tokenXMLText, "text/xml"); 41 | let nsResolver = xmlDoc.createNSResolver( xmlDoc.ownerDocument == null ? xmlDoc.documentElement : xmlDoc.ownerDocument.documentElement); 42 | let tokenNodes = xmlDoc.evaluate('/ts:token', xmlDoc, nsResolver, XPathResult.ANY_TYPE, null ); 43 | let tokenNode = tokenNodes.iterateNext(); 44 | 45 | expect(ts.getContractAddress(xmlDoc, "EntryToken", tokenNode, 3)).toMatchObject({'contractAddress':'0xFB82A5a2922A249f32222316b9D1F5cbD3838678','contractInterface':'erc875'}); 46 | expect(ts.getContractAddress(xmlDoc, "EntryToken", tokenNode, 42)).toMatchObject({contractInterface:'erc875',contractAddress:'0x2B58A9403396463404c2e397DBF37c5EcCAb43e5'}); 47 | }); 48 | 49 | // test("test getJSONAbi", () => { 50 | // 51 | // global.fetch = jest.fn((url) => 52 | // Promise.resolve({ 53 | // status: 400 54 | // }) 55 | // ); 56 | // 57 | // expect(ts.getJSONAbi('somecontract',{})).toBe(false); 58 | // 59 | // 60 | // 61 | // }); 62 | 63 | it('getJSONAbi file not found', () => { 64 | expect.assertions(1); 65 | 66 | global.fetch = jest.fn((url) => 67 | Promise.resolve({ 68 | status: 400 69 | }) 70 | ); 71 | 72 | return expect(ts.getJSONAbi('somecontract',{})).resolves.toEqual(false); 73 | 74 | }); 75 | 76 | it('getJSONAbi already added', async () => { 77 | expect.assertions(1); 78 | const data = await ts.getJSONAbi('somecontract',{somecontract:{val1: 2}}); 79 | expect(data).toMatchObject({val1: 2}); 80 | }); 81 | 82 | 83 | -------------------------------------------------------------------------------- /bootstrap-js/tests/modules.test.js-off: -------------------------------------------------------------------------------- 1 | let ts = require("../tokenscript-bootstrap"); 2 | 3 | test("Returns string query compared to object", () => { 4 | let str1 = "(&(objectClass=Person)(!(price<=4444))(|(locality=Jensen)(locality=Sydney)(locality=Babs J*)((locality=Sy*))))"; 5 | let str2 = "(&(!(price<=4444))(|(locality=Jensen)(locality=Sydney)(locality=Babs J*)((locality=Sy*))))"; 6 | // str = "locality=Sydney"; 7 | let props = { 8 | locality: 'Sydney', 9 | state: 'NSW', 10 | price: 15559 11 | } 12 | expect(ts.compareStringToProps(str1, props)).toBe("0"); 13 | expect(ts.compareStringToProps(str2, props)).toBe("1"); 14 | }); -------------------------------------------------------------------------------- /bootstrap-js/tests/tokenscript-bootstrap.test.js-off: -------------------------------------------------------------------------------- 1 | let ts = require("../tokenscript-bootstrap"); 2 | 3 | test("Returns string query compared to object", () => { 4 | let str1 = "(&(objectClass=Person)(!(price<=4444))(|(locality=Jensen)(locality=Sydney)(locality=Babs J*)((locality=Sy*))))"; 5 | let str2 = "(&(!(price<=4444))(|(locality=Jensen)(locality=Sydney)(locality=Babs J*)((locality=Sy*))))"; 6 | // str = "locality=Sydney"; 7 | let props = { 8 | locality: 'Sydney', 9 | state: 'NSW', 10 | price: 15559 11 | } 12 | expect(ts.compareStringToProps(str1, props)).toBe("0"); 13 | expect(ts.compareStringToProps(str2, props)).toBe("1"); 14 | }); -------------------------------------------------------------------------------- /bootstrap-js/ts.scss: -------------------------------------------------------------------------------- 1 | .ts_token_bar { 2 | position: absolute; 3 | top:20px; 4 | right: 20px; 5 | //border: 1px solid #eee; 6 | border-radius: 20px; 7 | background: #f5f5f5; 8 | 9 | &.opened { 10 | background-color: #c8dadf; 11 | outline: 2px dashed #92b0b3; 12 | outline-offset: -7px; 13 | padding-bottom: 10px; 14 | } 15 | 16 | 17 | &:not(.opened) { 18 | overflow: hidden; 19 | } 20 | 21 | &_icon { 22 | width: 40px; 23 | height: 40px; 24 | background: #f002; 25 | background-size: 30px 30px; 26 | background-repeat: no-repeat; 27 | background-position: center; 28 | cursor: pointer; 29 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='70' height='50' viewBox='0 0 70 50'%3E%3Cdefs%3E%3Cpath id='prefix__a' d='M0 0.07L48.973 0.07 48.973 49.874 0 49.874z'/%3E%3Cpath id='prefix__c' d='M0 49.93L249.973 49.93 249.973 0.126 0 0.126z'/%3E%3C/defs%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath fill='%23FFD400' d='M51.825 23.33c0 2.75-2.23 4.982-4.982 4.982-2.75 0-4.98-2.232-4.98-4.983 0-2.75 2.23-4.982 4.98-4.982 2.751 0 4.982 2.232 4.982 4.982'/%3E%3Cpath fill='%2381BA28' d='M56.382 20.857c-.06.094-.121.186-.179.28l-.001-.001c.059-.094.119-.185.18-.279'/%3E%3Cpath fill='%23FFF' d='M56.382 20.857c-.06.094-.121.186-.179.28l-.001-.001c.059-.094.119-.185.18-.279'/%3E%3Cpath fill='%2381BA28' d='M68.275 2.142c-.695 1.11-10.003 15.75-10.898 17.154-.207.326-.41.64-.608.955l-.387.605c-.972-3.759-4.108-6.65-8.002-7.26.177-.279 5.638-8.918 7.018-11.112.156-.253.296-.44.645-.438 4.017.013 8.03.013 12.047.016.036 0 .074.03.185.08'/%3E%3Cpath fill='%23E52F2E' d='M70.598 44.588c-2.01 2.386-4.275 4.07-7.153 4.747-3.952.929-7.72.508-11.093-1.935-2.455-1.774-3.95-4.257-5.037-7.02-.032-.081-.07-.178-.113-.294-.008-.022-.019-.045-.026-.069-.281-.74-.769-2.08-1.247-3.527-.389-1.182-.768-2.437-1.022-3.5.627.126 1.275.19 1.938.19 2.925 0 5.555-1.277 7.355-3.301l.022-.022c.663 1.642 1.351 4.22 2.006 6.222l.006.016c.179.547.353 1.052.529 1.483.726 1.814 1.498 3.588 2.956 4.936.097.089.199.176.302.262 1.9 1.574 4.172 1.984 6.544 2.061 1.288.043 2.566-.134 4.033-.25'/%3E%3Cg transform='translate(0 .055)'%3E%3Cmask id='prefix__b' fill='%23fff'%3E%3Cuse xlink:href='%23prefix__a'/%3E%3C/mask%3E%3Cpath fill='%234BBCEC' d='M38.509 28.53c-2.334 3.265-4.867 6.371-7.844 9.126-2.51 2.32-5.184 4.396-8.41 5.648-1.23.48-2.505.742-3.83.73-3.746.009-6.436-2.338-7.232-6.26-.707-3.485-.35-6.918.628-10.277 1.811-6.222 4.993-11.723 9.214-16.613 1.91-2.212 4.103-4.113 6.968-5.024 2.024-.643 4.063-.825 6.042.19 1.526.785 2.588 2.05 3.436 3.498 1.208 2.057 1.956 4.289 2.604 6.56 1.763-1.666 4.141-2.686 6.76-2.686.521 0 1.035.042 1.535.12l.593-.935c-.45-1.035-.83-1.975-1.26-2.89-1.339-2.852-3.17-5.318-5.807-7.098C38.776.508 35.302-.17 31.57.143c-4.755.396-9.191 1.78-13.245 4.282-6.44 3.982-11.497 9.31-15.045 16.005C1.005 24.715-.303 29.278.06 34.189c.47 6.335 3.662 10.866 9.34 13.636 4.417 2.156 9.118 2.35 13.882 1.765 3.958-.486 7.69-1.786 11.13-3.777 4.176-2.416 7.794-5.565 11.164-8.998.116-.12.19-.297.354-.38-.39-1.183-.768-2.437-1.02-3.5-2.69-.535-4.984-2.165-6.401-4.405' mask='url(%23prefix__b)'/%3E%3C/g%3E%3Cmask id='prefix__d' fill='%23fff'%3E%3Cuse xlink:href='%23prefix__c'/%3E%3C/mask%3E%3C/g%3E%3C/svg%3E%0A"); 30 | } 31 | & &_head { 32 | position: relative; 33 | &:after { 34 | opacity: 0; 35 | content: ''; 36 | display: block; 37 | background-color: #fff8; 38 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512' %3E%3Cpath fill='%23000' d='M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z'%3E%3C/path%3E%3C/svg%3E"); 39 | position: absolute; 40 | top: 0; 41 | left: 0; 42 | width: 100%; 43 | height: 100%; 44 | background-repeat: no-repeat; 45 | background-position: center; 46 | background-size: 25px; 47 | transition: opacity 0.3s; 48 | } 49 | &:hover:after { 50 | opacity: 1; 51 | } 52 | } 53 | &.opened &_head:after { 54 | transform: rotate(180deg); 55 | } 56 | &.opened { 57 | border-radius: 4px; 58 | width: 380px; 59 | //height: auto; 60 | } 61 | 62 | &.opened &_icon { 63 | opacity: 0 !important; 64 | display: none; 65 | } 66 | &:not(.opened) &_title, 67 | &:not(.opened) &_body 68 | { 69 | display: none; 70 | } 71 | &.opened &_title { 72 | text-align: center; 73 | font-size: 20px; 74 | padding: 10px; 75 | } 76 | & &_body 77 | { 78 | padding: 10px; 79 | min-height: 150px; 80 | .def { 81 | position: absolute; 82 | } 83 | .ts_token_wrap { 84 | //margin-top: 10px; 85 | margin-bottom: 10px; 86 | background: #fff; 87 | position: relative; 88 | box-sizing: border-box; 89 | .sidebar_toggle { 90 | padding: 2px 5px; 91 | background: #c8dadf; 92 | margin: 5px; 93 | //float: left; 94 | display: inline-block; 95 | border-radius: 4px; 96 | cursor: pointer; 97 | transition: all 0.5s; 98 | position: relative; 99 | &:hover { 100 | box-shadow: inset 0 -3.25em 0 0 #eee; 101 | } 102 | &:after { 103 | content: ''; 104 | position: absolute; 105 | display: block; 106 | width: 10px; 107 | height: 10px; 108 | top: -4px; 109 | right: -5px; 110 | background: red; 111 | border-radius: 100px; 112 | border: 1px solid #92b0b3; 113 | opacity: 0; 114 | transition: opacity 0.5s; 115 | } 116 | &.has_events { 117 | &:after { 118 | opacity: 1; 119 | } 120 | } 121 | } 122 | 123 | iframe { 124 | width: 100%; 125 | box-sizing: border-box; 126 | } 127 | .activity_cards { 128 | position: absolute; 129 | right: 100%; 130 | //width: 360px; 131 | background: #eee; 132 | top: 0; 133 | transform: translateX(-20px); 134 | padding: 10px; 135 | max-height: 85vh; 136 | overflow: auto; 137 | max-width: calc(100vw - 460px); 138 | &.opened { 139 | background-color: #c8dadf; 140 | outline: 2px dashed #92b0b3; 141 | outline-offset: -7px; 142 | padding-bottom: 10px; 143 | } 144 | & > div { 145 | min-width: 320px; 146 | padding: 0 5px; 147 | } 148 | &:not(.opened) { 149 | display: none; 150 | } 151 | .sidebar_toggle { 152 | display: none; 153 | } 154 | iframe { 155 | border: none; 156 | height: 45px; 157 | } 158 | h3 { 159 | margin-bottom: 0; 160 | margin-top: 0.2em; 161 | } 162 | } 163 | 164 | } 165 | 166 | .ts_connect { 167 | margin-top: 80px; 168 | } 169 | 170 | } 171 | 172 | } 173 | 174 | .ts_btn_std { 175 | padding: 2px 5px; 176 | background: #ccc; 177 | border-radius: 4px; 178 | cursor: pointer; 179 | transition: all 0.5s; 180 | text-align: center; 181 | &:hover { 182 | box-shadow: inset 0 -3.25em 0 0 #eee; 183 | } 184 | } 185 | /* 186 | #ts_select_token { 187 | width: 100%; 188 | display: flex; 189 | flex-direction: column; 190 | justify-content: center; 191 | align-items: center; 192 | position: fixed; 193 | max-height: 100vh; 194 | overflow-y: auto; 195 | background: #000c; 196 | height: 100vh; 197 | top: 0; 198 | left: 0; 199 | opacity: 1; 200 | transition: opacity 0.3s; 201 | z-index: 10; 202 | &.disabled { 203 | opacity: 0; 204 | pointer-events: none; 205 | } 206 | .ts_select_box { 207 | padding: 30px; 208 | background: #fff; 209 | border-radius: 4px; 210 | border: 1px solid #000; 211 | width: 300px; 212 | text-align: center; 213 | position: relative; 214 | .ts_close { 215 | position: absolute; 216 | top: -10px; 217 | right: -10px; 218 | display: block; 219 | width: 25px; 220 | height: 25px; 221 | border-radius: 50%; 222 | border: 2px solid #9e9e9e; 223 | background: #fff; 224 | background-position: center; 225 | background-repeat: no-repeat; 226 | background-size: 13px; 227 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512' %3E%3Cpath fill='currentColor' d='M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z' class=''%3E%3C/path%3E%3C/svg%3E"); 228 | cursor: pointer; 229 | } 230 | h2 { 231 | //color: #fff; 232 | 233 | } 234 | 235 | .ts_token { 236 | border: 1px solid #eee; 237 | background: #f5f5f5; 238 | padding: 10px 20px; 239 | font-size: 20px; 240 | width: 100%; 241 | margin-top: 10px; 242 | text-align: center; 243 | box-sizing: border-box; 244 | transition: 0.25s; 245 | cursor: pointer; 246 | &:hover { 247 | //box-shadow: 0 0.5em 0.5em -0.4em #eee; 248 | //transform: translateY(-0.25em); 249 | box-shadow: inset 0 -3.25em 0 0 #eee; 250 | } 251 | } 252 | } 253 | } 254 | */ 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | .box 263 | { 264 | font-size: 1.25rem; /* 20 */ 265 | background-color: #c8dadf; 266 | position: relative; 267 | padding: 40px 20px; 268 | text-align: center; 269 | outline: 2px dashed #92b0b3; 270 | outline-offset: -10px; 271 | -webkit-transition: outline-offset .15s ease-in-out, background-color .15s linear; 272 | transition: outline-offset .15s ease-in-out, background-color .15s linear; 273 | 274 | max-width: 500px; 275 | width: 100%; 276 | } 277 | 278 | .box.is-dragover 279 | { 280 | outline-offset: -20px; 281 | outline-color: #c8dadf; 282 | background-color: #fff; 283 | } 284 | .box__dragndrop, 285 | .box__icon 286 | { 287 | display: none; 288 | } 289 | .box.has-advanced-upload .box__dragndrop 290 | { 291 | display: inline; 292 | } 293 | .box.has-advanced-upload .box__icon 294 | { 295 | width: 100%; 296 | height: 80px; 297 | fill: #92b0b3; 298 | display: block; 299 | margin-bottom: 40px; 300 | } 301 | 302 | .box.is-uploading .box__input, 303 | .box.is-success .box__input, 304 | .box.is-error .box__input 305 | { 306 | visibility: hidden; 307 | } 308 | 309 | .box__uploading, 310 | .box__success, 311 | .box__error 312 | { 313 | display: none; 314 | } 315 | .box.is-uploading .box__uploading, 316 | .box.is-success .box__success, 317 | .box.is-error .box__error 318 | { 319 | display: block; 320 | position: absolute; 321 | top: 50%; 322 | right: 0; 323 | left: 0; 324 | 325 | -webkit-transform: translateY( -50% ); 326 | transform: translateY( -50% ); 327 | } 328 | .box__uploading 329 | { 330 | font-style: italic; 331 | } 332 | .box__success 333 | { 334 | -webkit-animation: appear-from-inside .25s ease-in-out; 335 | animation: appear-from-inside .25s ease-in-out; 336 | } 337 | @-webkit-keyframes appear-from-inside 338 | { 339 | from { -webkit-transform: translateY( -50% ) scale( 0 ); } 340 | 75% { -webkit-transform: translateY( -50% ) scale( 1.1 ); } 341 | to { -webkit-transform: translateY( -50% ) scale( 1 ); } 342 | } 343 | @keyframes appear-from-inside 344 | { 345 | from { transform: translateY( -50% ) scale( 0 ); } 346 | 75% { transform: translateY( -50% ) scale( 1.1 ); } 347 | to { transform: translateY( -50% ) scale( 1 ); } 348 | } 349 | 350 | .box__restart 351 | { 352 | font-weight: 700; 353 | } 354 | .box__restart:focus, 355 | .box__restart:hover 356 | { 357 | color: #39bfd3; 358 | } 359 | 360 | .js .box__file 361 | { 362 | width: 0.1px; 363 | height: 0.1px; 364 | opacity: 0; 365 | overflow: hidden; 366 | position: absolute; 367 | z-index: -1; 368 | } 369 | .js .box__file + label 370 | { 371 | max-width: 80%; 372 | text-overflow: ellipsis; 373 | white-space: nowrap; 374 | cursor: pointer; 375 | display: inline-block; 376 | overflow: hidden; 377 | } 378 | .js .box__file + label:hover strong, 379 | .box__file:focus + label strong, 380 | .box__file.has-focus + label strong 381 | { 382 | color: #39bfd3; 383 | } 384 | .js .box__file:focus + label, 385 | .js .box__file.has-focus + label 386 | { 387 | outline: 1px dotted #000; 388 | outline: -webkit-focus-ring-color auto 5px; 389 | } 390 | .js .box__file + label * 391 | { 392 | /* pointer-events: none; */ /* in case of FastClick lib use */ 393 | } 394 | 395 | .no-js .box__file + label 396 | { 397 | display: none; 398 | } 399 | 400 | .no-js .box__button 401 | { 402 | display: block; 403 | } 404 | .box__button 405 | { 406 | font-weight: 700; 407 | color: #e5edf1; 408 | background-color: #39bfd3; 409 | display: block; 410 | padding: 8px 16px; 411 | margin: 40px auto 0; 412 | } 413 | .box__button:hover, 414 | .box__button:focus 415 | { 416 | background-color: #0f3c4b; 417 | } 418 | 419 | .box { 420 | .submit_btn { 421 | font-weight: 700; 422 | color: #e5edf1; 423 | background-color: #39bfd3; 424 | display: block; 425 | padding: 8px 16px; 426 | margin: 20px auto 0; 427 | cursor: pointer; 428 | } 429 | } 430 | 431 | .options-box { 432 | outline: 2px dashed #92b0b3; 433 | outline-offset: -10px; 434 | -webkit-transition: outline-offset .15s ease-in-out, background-color .15s linear; 435 | transition: outline-offset .15s ease-in-out, background-color .15s linear; 436 | max-width: 500px; 437 | width: 100%; 438 | margin-top: 20px; 439 | font-size: 1.25rem; 440 | background-color: #c8dadf; 441 | position: relative; 442 | padding: 20px; 443 | text-align: center; 444 | textarea { 445 | width: 100%; 446 | box-sizing: border-box; 447 | height: 6em; 448 | padding: 0.2em; 449 | line-height: 1.4em; 450 | } 451 | } 452 | 453 | .remote-download-box { 454 | input { 455 | width: 100%; 456 | box-sizing: border-box; 457 | } 458 | } -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | CJK_FONT=AR PL UMing TW 2 | PANDOC_OPTIONS=-V geometry:margin=1.5in 3 | 4 | # Pandoc uses rsvg-convert to embed SVG in PDF files. 5 | # Make sure that command is installed. 6 | 7 | papers : short_paper.pdf design_paper.pdf 8 | 9 | short_paper.pdf : short_paper.md 10 | pandoc $(PANDOC_OPTIONS) -o short_paper.pdf short_paper.md 11 | design_paper.pdf : design_paper.md 12 | # the design paper has some ideographs 13 | pandoc $(PANDOC_OPTIONS) -V CJKmainfont="$(CJK_FONT)" --pdf-engine=xelatex -o design_paper.pdf design_paper.md 14 | -------------------------------------------------------------------------------- /doc/The tech Stack.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartTokenLabs/TokenScript/0581e9c0448483c71555139c537cde9c1e99ac28/doc/The tech Stack.docx -------------------------------------------------------------------------------- /doc/articles/We need a better framework for applications using blockchain 中文: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /doc/articles/data-object.md: -------------------------------------------------------------------------------- 1 | # The way we treat data on the blockchain is wrong—this is how it’s supposed to work (pt1) 2 | 3 | Here in the TokenScript project, we are adapting tried-and-true methods (that have borne fruit in cryptography engineering for decades) to public blockchains. 4 | 5 | Unfortunately, data on the blockchain is not being used for its intended purpose. People are blaming Ethereum “congestion” while writing smart contracts that forward chit-chat messages. But having a blockchain strewn with "crypto-graffiti" isn’t even the biggest problem. 6 | 7 | The biggest problem, in our view, is that the data simply is not structured for interoperability, extensibility and longevity. 8 | 9 | In other words, the data’s structure is tailored for use in only one token project —— this specificity tends to cause problems in other projects that uses the token. Many data structures are abandoned as changes are introduced, and messages already signed can't be used. 10 | 11 | I’ll start with an example data object, illustrate how it could be treated differently, then introduce you to the TokenScript’s data-object (which is a work in progress). 12 | 13 | ## Example of data objects 14 | 15 | We’ll start with an example, which we’ll later generalise. 16 | 17 | Imagine an event ticket as a data object, encoded in JSON: 18 | 19 | ![JSON representation of a ticket data object](data-object-ticket.svg) 20 | 21 | This object, a ticket, carries the following information: 22 | 23 | 1. This is the 24th ticket issued. 24 | 2. The ticket is “class 2” (analogous to a VIP-class ticket). 25 | 3. The event commences on January 1, 2020 at 20:00. 26 | 27 | Such a data object may be used in a blockchain transaction. Suppose say we have an Ethereum smart contract that transfers a ticket’s ownership: 28 | 29 | function transfer_ticket(string ticket, address newOwner) 30 | 31 | Having the data object in JSON format will consume a lot of “gas,” however, because it increases the transaction size and the required effort for smart contract parsing. The data object needs to be tightly packed, and to make that happen, we must first separate the data from its schema. 32 | 33 | ## Separation of data and schema 34 | 35 | For the *data*, we encode it into this 20 bytes: 0x3012020118020102180A32303230303130313230. Observe how the 20 bytes contained the 3 pieces of information: 36 | 37 | 0x30 12 02 01 18 0A 01 02 18 0A 32 30 32 30 30 31 30 31 32 30 38 | -- -- +---------------------------- 39 | ^ ^ ^ 40 | 24 2 2 0 2 0 1 0 1 0 2 0 41 | 42 | Observe the ticket number `24` is encoded as `0x18`; the ticket class "VIP" encoded as `0x02`; the date is encoded in an ASCII string. The in-between structural-bytes are the result of using standard DER encoding rules†. 43 | 44 | For the *schema*, we write it in ASN.X (an XML schema language). 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | Schema has to be stored outside of the smart contract. In our TokenScript project the schema is stored in TokenScript so that wallets and dapp browsers compatible with TokenScript can use the schema to understand the 20 bytes of data. You can store it in other formats‡ but you shouldn’t store it directly in a smart contract, instead, the schema can be *compiled* into a terse piece of solidity bytecode for the smart contract to parse the 20 bytes.[^2] 59 | 60 | ## Using the schema 61 | 62 | Once we have separated the data from its schema, our Ethereum smart contract function becomes: 63 | 64 | function transferTicketing(bytes ticket, address newOwner) 65 | 66 | Where `string` is replaced by `bytes` to accept the DER-encoded 20 bytes. 67 | 68 | Let's take a closer look by showing a few lines before and after the function declaration: 69 | 70 | struct Ticket { 71 | uint numero; // the sequence number of the ticket 72 | uint class; // 0: normal, 1: gifted, 2: VIP 73 | string start; // start time of the event 74 | } 75 | 76 | function transferTicketing(bytes ticket, address newOwner) 77 | { 78 | Ticket ticketObj = parse_ticket(ticket); 79 | ... 80 | } 81 | 82 | The function `parse_ticket` consists of code compiled from the schema. This is considerably more efficient than a JSON parser. 83 | 84 | On the other hand, should a dapp need to construct such DER-encoded bytes for use in a transaction, or should the user’s wallet need to display an already-constructed transaction in a user-readable way, it can rely on the schema. 85 | 86 | ## Why the fuzz? 87 | 88 | So what's the advantage of this DER or ASN fuzz over the following straightforward, newbie-friendly method? 89 | 90 | function transferTicket(uint numero, uint class, string start, address newOwner) 91 | 92 | Or this more structured version? 93 | 94 | function transferTicket(Ticket ticket, address newOnwer) 95 | 96 | Is it just for making the transactions shorter? 97 | 98 | The answer to both these questions is “yes,” and there’s more to that “yes” than one may assume. At the outset, this decreases transaction payload by more than 50%, albeit with more advantages: 99 | 100 | 101 | ## Reason 1: Attestation 102 | 103 | A string of DER-encoded bytes is useful for signing. Why do you need to sign it, though? 104 | 105 | To get an attestation. We call signed data objects "attestation," since it's the signer attesting to something. 106 | 107 | Let's examine the ticket example again. At first, you might imagine that a ticket contract holds all tickets and information regarding their ownership. For example, when "Alice" transfers a ticket to "Bob," Alice initiates a transaction which reassigns the ownership of that ticket to Bob. 108 | 109 | An event organiser might issue thousands, if not tens of thousands of tickets for an event, and most of the people who receive the issued tickets will not then transfer them to someone else. If there isn't a transfer scenario, the ticket need not appear in the blockchain. The event organiser can sign an attestation, where they attest the ticket's ownership to a certain Ethereum key holder. The keyholder can prove their ownership via a challenge-response protocol. 110 | 111 | On the other hand, if data is encoded in JSON, it can’t be signed safely since JSON is not a deterministic encoding. 112 | 113 | Attestation is generally useful. Here are a few examples. 114 | 115 | - You can write a smart contract in such a way that a user attested to be a Sophisticated Investor can make purchases in an ICO pre-sale. 116 | 117 | - A car insurance company can attest to the fact that your car (represented here by an Ethereum token) is insured. 118 | 119 | - If your car is a smart vehicle and you authorises a friend to drive it, you can write an attestation without sending an Ethereum transaction. 120 | 121 | The schema for signing of attestations should be adapted from existing standards, rather than inventing new ones and recreate security issues already solved in existing standards. TokenScript is [working on such an schema adaptation](https://community.tokenscript.org/t/weekly-design-meeting-16-simple-attestation-format/302). There are much further work to be done. For example, we need a format that can do partial attestation by using Merkle Tree, or even zero-knowledge proof of attestation. 122 | 123 | ## Reason 2: Data-Interoperability 124 | 125 | DER-encoded data has better interoperability. Continuing with the ticket attestation example: which systems need to use the attestation? We already know about the following. 126 | 127 | 1. **Smart contracts**. If Alice wishes to sell her ticket (in the form of an attestation), the smart contract needs to check the event organiser’s signature. 128 | 129 | 2. **Wallets**. The attestation’s contents must be properly understood for a correct display in a user’s wallet. Should any transaction involve the attestation, it will inform the user of what’s in the transaction. (Dapp browsers have the same need.) 130 | 131 | That’s not all. The event organiser’s website also needs to know how to read the attestation, since a ticket holder may use it to log in to a website to check for updates. The doorman (or a turnstile, their modern equivalent) needs to read this data (perhaps through a QR code). 132 | 133 | Even the Embassy and border police use these attestations—throughout the last year, fully-attested FIFA tickets (called Fan ID) were used to cross the Russian border in lieu of a VISA. 134 | 135 | It’s not difficult to recognize that there are many systems interested in attestations, and these systems are heterogeneous. For example, a smart contract belongs to blockchain, and a Wallet belongs to mobile app. When it comes to the event organiser’s website, JSON representation may be needed. The turnstile at the gate is an IoT system which accepts QR codes. Since on the system depends on a verified signature, it can’t be converted it at will —— a uniformed presentation of the signed data would be necessary in that circumstance. 136 | 137 | Should there be any alteration of the data schema (such as, for instance, the introduction of an additional ticket class called “VVIP”), one can’t reasonably expect all these decentralised systems to be updated together. If the data is schema-driven, however, the schema can be updated to facilitate a relatively easy update. 138 | 139 | You may think your token ought not to be used across so many systems. The reality is: one can never be sure. One of the major strengths of decentralised platform like Ethereum is the capacity for buidl. There are more than a few systems which uses DAI were developed without MakerDAO’s authorisation. Such buidl wouldn’t be possible with traditional centralised systems like American Express Connect. 140 | 141 | ## Reason 3: Extensibility 142 | 143 | Extensibility is tightly connected to interoperability. Always bear in mind that once data is signed, it can’t be “converted” for the consumption of a new system without invalidating its signature in the process. Thus, the systems must be built to understand old and new data alike (even if their schema has drifted in time). 144 | 145 | Suppose you are the beneficiary of a will that is in the form of crypto attestation. Once your parents pass away, you are ready to cash it out. You certainly don’t want the will contract, after undergoing a variety of upgrades throughout the years your parents lived, to reject the attestation and ask that it be signed again by your (now deceased) parents within a new data structure! 146 | 147 | One data structure that stood the test of time is X.509 certificates. It was invented before SSL, and its underlying data structure survived until now. X.509 certificate is designed as an ASN.1 module, which has built-in support for extensibility. 148 | 149 | Today’s blockchain data objects should also boast this support. 150 | There isn’t enough time to cover how this is done, but to summarize, extensibility depends on schema. For instance, a nicely defined schema enables one to perform tasks such as extending an array of data into a 2-dimensional matrix as required. 151 | 152 | # Where are we going from here? 153 | 154 | Here in TokenScript project we treat data correctly by adapting existing standards. TokenScript itself is designed to be standardized and is being mentored by OASIS (the same standard group behind Open Document ISO standards). To participate you can join: 155 | 156 | - [TokenScript Forum](http://community.tokenscript.org/) 157 | - Participate [the design meeting on Google Hangout Meet](https://meet.google.com/yix-kjmv-gsj) every Thursday 7pm Sydney time 158 | - If you live near Melbourne, Australia, participate [the meet-up on 22th Nov](https://meet.google.com/yix-kjmv-gsj) 159 | - Browse the [TokenScript project website](http://tokenscript.org) and the github repository (linked from that website). 160 | 161 | [^1]: DER is the default way to encode data in cryptography engineering. For example, Bitcoin signatures and X.509 certificates are encoded this way. If you want to inspect DER encodeded data, you can use openssl like this: 162 | 163 | $ echo -n 0x3012020118020102180A32303230303130313230 | xxd -r -p | openssl asn1parse -inform DER -i 164 | 0:d=0 hl=2 l= 18 cons: SEQUENCE 165 | 2:d=1 hl=2 l= 1 prim: INTEGER :18 166 | 5:d=1 hl=2 l= 1 prim: INTEGER :02 167 | 8:d=1 hl=2 l= 10 prim: GENERALIZEDTIME :2020010120 168 | 169 | [^2]: The same schema can be written in a equivalent shorthand format called ASN.1: 170 | 171 | ```` 172 | SEQUENCE { 173 | numero INTEGER, 174 | class ENUMERATED { normal(0), gifted(1), vip(2) }, 175 | date UTCTime 176 | } 177 | ```` 178 | We choose to use XML so we can extend it to allow encoding in ABI as well. 179 | 180 | # The way we treat data on the blockchain is wrong—this is how it’s supposed to work (pt2) 181 | 182 | In my last article I made the following points; 183 | 184 | - Data on the blockchain should be extensible with a schema that can be signed; 185 | - Data itself should be tight and signed. 186 | - -------------------------------------------------------------------------------- /doc/attribute-processing.md: -------------------------------------------------------------------------------- 1 | # Token Attributes 2 | 3 | A. I describe how to think of attributes and their origins and mappings as a pipeline below. A useful case to think through is a function origin which returns a GeneralisedTime (so we set as="utf8"), but the attribute syntax is GeneralisedTime (and hence the JavaScript object is expected to be our standard JavaScript GeneralisedTime class). 4 | 5 | B. Based on this, it would be good if we add as="address". The matching syntax type should be IA5 String (but Directory String will work too). We can return the JavaScript object as a string or if there's a suitable syntax that defines it as an address, we can create a simple class to wrap it (in the future?) 6 | 7 | C. To preserve precision, Numeric Strings are stored internally as a Big Integer, but returned as a JavaScript string (unless we want to support provide or endorse a big number library. Possibly later?) 8 | 9 | D. Note that according to https://tools.ietf.org/html/rfc4517#section-3.3.23, Numeric String is: 10 | 11 | ``` 12 | NumericString = 1*(DIGIT / SPACE) 13 | DIGIT = %x30 / LDIGIT ; "0"-"9" 14 | LDIGIT = %x31-39 ; "1"-"9" 15 | ``` 16 | 17 | Attribute as a pipeline: 18 | 19 | One way to look at an attribute type and their origin, and (optional) mapping is as a pipeline of 2 or 3 processing stages. 20 | 21 | With a mapping: 22 | 23 | ``` 24 | A. Origin output with 25 | specific type (based on 26 | Inputs with "as"). Attribute 27 | specific B. Convert to string to be value with 28 | types used as mapping's input specific 29 | ─────────────▶ ┌──────────┐ ┌──────────┐ ┌──────────┐ type 30 | │ Origin │───────────▶ │ Mapping │──────────▶│ Syntax │─────────▶ 31 | ─────────────▶ └──────────┘ └──────────┘ └──────────┘ 32 | 33 | Mapping output is 34 | always a string. 35 | 36 | 37 | ``` 38 | 39 | 40 | Without mappings: 41 | 42 | ``` 43 | Inputs with Origin output with specific Attribute 44 | specific type (based on "as") value with 45 | types specific 46 | ─────────────▶ ┌──────────┐ ┌──────────┐ type 47 | │ Origin │───────────▶ │ Syntax │─────────▶ 48 | ─────────────▶ └──────────┘ └──────────┘ 49 | 50 | 51 | 52 | ``` 53 | 54 | 1. Each stage has 0 or 1 input (or in the case of function origins, 0 or more inputs) and 1 output 55 | 56 | 2. All input and output are typed. 57 | 58 | 3. Ethereum function origins 59 | 60 | 1. A ethereum function origin's input types are already specified as Solidity types, eg. `` 61 | 2. Ethereum function origin's output type are specified with `as`, with each possible value mapping to exactly 1 Solidity type. Specifically: 62 | 63 | * as="uint" => uint256 64 | * as="int" => int256 65 | * as="utf8" => string 66 | * as="e18" => uint256 67 | * as="bool" => bool 68 | 69 | This type mapping is used to interpret (and validate) the smart contract function call return value. 70 | 71 | 4. User entry origin's output type is specified with `as`, eg. as="e18" and as="uint8" 72 | 73 | 5. Token ID-based origins' output type is specified with `as` similarly to user entry origins 74 | 75 | 6. Similar to ethereum function origins, user entry and token ID-based origins should use the `as` value to interpret the output 76 | 77 | 7. Mappings 78 | 79 | 1. Mappings always have string as the input type and output type 80 | 2. The output from the origin is converted to a string which is used as the key for the mapping 81 | 3. The output value is obtained, as a string 82 | 83 | 8. Syntax 84 | 85 | 1. The syntax stage takes the output from the mapping (which is typed string), if there is one. 86 | 2. The syntax stage takes the output from the origin, with a specific type in the absence of a mapping, as its input 87 | 3. The syntax stage converts the input to the output with a type based on the syntax: 88 | 89 | * Directory String, IA5 String => "string" (and JavaScript string) 90 | * Generalised Time => GeneralisedTime internally (and our standard Javascript GeneralisedTime type) 91 | * Integer => Big Integer internally (and JavaScript number) 92 | * Boolean => bool internally (and Javascript bool) 93 | * Numeric String => Big Integer internally (and JavaScript string) 94 | 95 | 9. This brings some consistency so we can expand the pipeline to more than 3 stages in the future 96 | 97 | 10. This is also useful when we have multiple origins which different types within the same attribute type 98 | 99 | 11. In ``, I just assume the function is an origin with as="void" (void is only used internally for consistency), so the output (if any) is dropped. In the future if we need to chain function calls (I don't know if we will ever need to), then it's just another pipeline of function origins with non-void as="" 100 | 101 | 12. Note while we say the input and output types are typed, TokenScript clients can still hold them internally as strings as long as it knows and can enforce the types they represent. That's an implementation detail. But such a case (assuming `getStreet()` returns a string) should not be permitted: 102 | 103 | ``` 104 | 105 | 106 | 107 | 108 | Another Street 109 | 110 | 111 | 112 | ``` 113 | 114 | But this is valid: 115 | 116 | ``` 117 | 118 | 119 | 120 | 121 | Another Street 122 | 123 | 124 | 125 | ``` 126 | -------------------------------------------------------------------------------- /doc/authenticator.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartTokenLabs/TokenScript/0581e9c0448483c71555139c537cde9c1e99ac28/doc/authenticator.dia -------------------------------------------------------------------------------- /doc/authenticity+trustworthiness.md: -------------------------------------------------------------------------------- 1 | ## Authenticity and trustworthiness of a TokenScript 2 | 3 | (This document is not about the runtime security of Token Enclave - which will be documented separately. This deals with the authenticity and trustworthiness of a TokenScript on a textual level.) 4 | 5 | A TokenScript file can be signed. A TokenScript can also be trusted. Ideally, both should be done. 6 | 7 | At the moment, TokenScript is trusted through an "Express-of-Trust" transaction from the smart contract deployment key. In the future, a smart contract might be able to return the signing key needed for TokenScript. Both should work towards asserting the relationship between a Smart Contract and a TokenScript, hence establishing trust. 8 | 9 | However, trust coming from smart contract alone isn't sufficient to the end user, since the user has otherwise no way to assert if the smart contract is the one he intends to interact with, or a phishing contract. But when that trust is not available, having the TokenScript signed helps the end-user to ascribe trust by authenticity. 10 | 11 | - If a TokenScript is trusted, through an Express-of-Trust transaction, then the Smart Contract owner has either created or read the TokenScript and recommended the end-users to use it. 12 | 13 | - If a TokenScript is signed, the end-user can ascribe authenticity (the author and integrity), and if that author is known to the end-user, he is likely to trust it without the Express-of-Trust; otherwise, he makes his own decision. 14 | 15 | Or in other words: 16 | 17 | - signing a TokenScript establish authenticity of the TokenScript but not how much the token contract trusts it; 18 | 19 | - the express-of-trust transaction expresses that the token contract author trusts the TokenScript, but nothing about authenticity (who wrote it). 20 | 21 | Or in layman's terms: 22 | 23 | - Signing a TokenScript "connects" it to identity (e.g. a website, an organisation), with no guarantee if that identity is of a trustworthy entity. 24 | - Express-of-trust claims the TokenScript trustworthy, without connecting it to any identity. 25 | 26 | Both have their uses. If you are a token contract author, you can do express-of-trust to a TokenScript; however, there are cases that signed TokenScripts are needed: 27 | 28 | Case 1: 29 | 30 | A token issuer (custodian) want to reassure a user who trusts it by an external (non-blockchain) identity (e.g. website domain name) that he/she is using the correct contract. For example, a token issuer changed its main contract after discovering some security exploitation. It knows that its user trust it by domain name, so it might issue a token script that is signed by the same domain name with expressed trust from both the old and new contract to help the user to migrate. 31 | 32 | Case 2: 33 | 34 | A token issuer discarded his deployment key (or lost it), but still wish to let its user trust the TokenScript they provided by the help of providing authenticity (revealing the authorship). 35 | 36 | Case 3: 37 | 38 | A token issuer did not write a TokenScript, but a 3rd party did it; or that the 3rd party has provided additional features. If the 3rd party gained trust (e.g. through social media or within a community), it can sign and release its own TokenScript, and the users can accept them by the reputation of the 3rd part without the token contract's Express-of-Trust. 39 | 40 | (The cases of "mixed configuration", like when a 3rd party writes a TokenScript providing action in addition to the one written by the token issuer, is not covered in this introductory document. Instead, they are covered in the future modularisation documents.) 41 | 42 | ## Signed 43 | 44 | A signed TokenScript file is a TokenScript file signed through the use of an XML Signature. Any signature signing algorithm supported by XML Signature (including ECDSA supported through XML Signature 1.1) is accepted. 45 | 46 | At this moment, user agents should only accept such XML Signatures if certified by a certificate authority (CA). For convenience, a website certificate will do at this moment. In the years to come, implementations might require certificates from issuers who can validate an organisation or various forms of decentralised certificate issuers. 47 | 48 | When a signed TokenScript is used, the user agent displays the domain name or organisation name of the certificate. It's up to the user if they allow the use of such a TokenScript. We expect users to assert judgement based on whether or not they recognise the CommonName of the certificate. 49 | 50 | Revocation follows the same principles as these certificates normally do. 51 | 52 | ### Signing of external data 53 | 54 | Any data reference - icons, images and language packs - used by the TokenScript must be referred to in the `` section of the XML signature using `` element, or they will be considered not available (TokenScript implementations decide how to treat unavailable data). Each reference is downloaded and its digest verified as part of the TokenScript signature verification process. If any of the references fail to download or the digest doesn't match, the entire XML signature is bad. 55 | 56 | ## Trusted (Ethereum) 57 | 58 | A trusted TokenScript file is one explicitly trusted by the author of a token contract on Ethereum. Such trust is expressed by an Ethereum transaction. Only one signing algorithm, ECDSA (secp256k1), is used in express-of-trust because trust is expressed by an Ethereum transaction. 59 | 60 | Such trust is also revoked by a transaction. 61 | 62 | The transaction consists of a simple To address, with no data attached. 63 | 64 | ### The "from" address 65 | 66 | If the smart contract has no key-management on its own, the transaction should be sent from the smart contracts deployment key. If the smart contract has a key management function, the transaction should be sent from the current administrative key. 67 | 68 | ### The "to" address 69 | 70 | - to express trust, send any amount (e.g. 0Ξ) to a special address that represents the trust from the token contract (𝑡) to the TokenScript. 71 | 72 | - to express revocation, send any amount (e.g. 0Ξ) to another special address that represents the revocation of such trust. 73 | 74 | The express-of-trust special address is calculated in this way: 75 | 76 | TokenScript project's donation address is: `0x000bd52fb4f46f148b0ff0cc651048e283d2d000`. Its public key is *Y*, its value is: 77 | 78 | 0x483d69cc4377d318da81402f2488f588ccae3d22a37be36457a574487d9ca4d9c38cd62d83113e440c7bdc682ced6d05ee739b831c6d5cb01982367f76fc8ce0 79 | 80 | In multiplicative group notation, its value is 𝑔ˣ where 𝑥 is the private key held by TokenScript administration. 81 | 82 | First, we obtain a SHA256 digest 𝑑 from the exclusive canonicalization of the TokenScript. If this TokenScript happens to be signed as well, and the `` used for its root is SHA256 (it usually is), you can find the value encoded in base64 in the `` if it is signed. (Of course, you need to calculate this value if the TokenScript isn't signed.) 83 | 84 | Then compute *h=H(𝑡|"TRUST"|𝑑)* where *H* denotes Keccak. `|` is used to denote concatenation. The text *TRUST* is simply an ASCII encoding of the literal word TRUST. 85 | 86 | Then, we generate the secp256k1 elliptic curve point Yʰ, and hash it to get an address. This is the special address for express-of-trust of this specific TokenScript. 87 | 88 | This address can be independently generated by TokenScript implementations, without the knowledge of 𝑥. Hence trust can be verified independently. In the meanwhile, if an implementation has the TokenScript file, it can calculate the private key of that address by 𝑥·ℎ given that (𝑔ˣ)ʰ = (gʰ)ˣ. 89 | 90 | For revocation, the idea is the same, except the point used for the special address is now *h=H(𝑡|"REVOKE"|s)*. 91 | 92 | ## difference between trusted and signed 93 | 94 | ### Symantec: signing express authorship, not trust 95 | 96 | A signed TokenScript is no more trustworthy than the signer identified by CommonName. It has no cryptographically provable tie to the token itself. For example, we know by reputation that MakerDAO has the stewardship of the DAI token, so a TokenScript for DAI Token signed by MakerDAO.com is probably trustworthy. 97 | 98 | In other words, whether or not a signed TokenScript is trustworthy is the judgement of the user. 99 | 100 | Express-of-trust is stronger and more direct. The key holder which deployed (or manages) the DAI token contract explicitly expressed trust to that TokenScript by sending a transaction. It's time locked in a blockchain. 101 | 102 | It's worth noticing that Express-of-trust isn't authorship - a smart contract author can express trust to a signed TokenScript authored by someone else. While signed a TokenScript implies authorship. 103 | 104 | ### Signing isn't on the file; trust is 105 | 106 | A signed TokenScript is signed by an XML signature which usually is (but not required to) be embedded in an XML file. The cryptographic signature is not in the file; it's in the `` element of the XML Signature. The XML signature is not in the file either, it's on a collection of references which typically contains the root node of the TokenScript. 107 | 108 | ### Signing doesn't use Keccak at all; express of trust only uses Keccak 109 | 110 | Keccak isn't defined as a valid hashing method in XMLSIG11, so it can't be used in an XML Signature without extending the w3 recommendation behind it, which leads to trouble when validating with existing tools. Therefore we restrict the signing of TokenScript to the algorithms allowed in XMLSIG11. 111 | 112 | For the same reason, since developers might be habitually using Keccak in blockchain applications, we use Keccak to hash the TokenScript as well as the express-of-trust. 113 | 114 | -------------------------------------------------------------------------------- /doc/data+filter.md: -------------------------------------------------------------------------------- 1 | # Data-objects and Tokens 2 | 3 | This article serves to tell the difference between a token (defined with a `` element) and data-objects (defined with a `` element). I will start by giving examples and end with a definition. 4 | 5 | Imagine a token with these attributes: 6 | 7 | { 8 | "objectClass": "CarToken", 9 | "ownerAddress": "0xdecafbad", 10 | "colour": "red", 11 | "rego": "4JHLC", 12 | "VIN": "KL3TA48E9EB541191" 13 | } 14 | 15 | It might be associated with a few types of objects, the owner of the car, lends it to Bob for 3 days, Alice will create an authorisation, which is an artifact with properties: 16 | 17 | { 18 | "objectClass": "AuthorisationToUse", 19 | "car": "rego=4JHLC", 20 | "user": "Bob", 21 | "start": 2019-09-09, 22 | "end": 2019-09-10, 23 | "allow": ["drive", "refuel", "service", "park", "locate"], 24 | "deny": ["collateralise", "sell", "trade", "lease"], 25 | "issuer": 0x809384902...80957FF # this is an Operational property derived from the signature 26 | } 27 | 28 | Let's move to a more advanced scenario of objects derived from the original CarToken. Let's say Alice collateralises it to get a loan, a *CollateralisedDebtObligation* token would be created: 29 | 30 | { 31 | "objectClass": "CollateralisedDebtObligation", 32 | "collateralType": "CarToken", 33 | "collateral": "VIN=KL3TA48E9EB541191", 34 | "value": 35, 35 | "CreateTimeStamp": 2019-09-09, 36 | "maturity": 2020-09-09 37 | } 38 | 39 | The two cases (authorisation and *CollateralisedDebtObligation* token) look alike, but this time the new thing is called a token, not an data-object. There are 2 reasons. 40 | 41 | First, authorisation is an artifact depending on the *CarToken*. It doesn't have to affect the status of the *CarToken*. For example, on Alice's mobile phone, the authorisations can be displayed below the car token in a list. Alice can authorise her entire family to use the car with several authorisations or a multi-user authorisation. 42 | 43 | On the other hand, *CollateralisedDebtObligation*, also depending on the *CarToken*, affects the status of the *CarToken*, since a collateralised car can't be collateralised again, nor can it be sold (without changing the loan). On Alice's mobile phone, the car might be displayed with a "collateralised" tag, together with a *CollateralisedDebtObligation* token to remind her of the payment obligation and allow her to do things like withdrawing from its line of credit. One can imagine that in this case the car token is extended to include its collateralisation. Since json doesn't support namespace, I'll use a comment to denote the extended attribute: 44 | 45 | { 46 | "objectClass": "CarToken", 47 | "ownerAddress": "0xdecafbad", 48 | "colour": "red", 49 | "rego": "4JHLC" 50 | "VIN": "KL3TA48E9EB541191", 51 | "collateralised": TRUE # look up CollateralisedDebtObligation token contract 52 | } 53 | 54 | Therefore it can be used to affect the available actions of a *CarToken*, since the condition for "trade" action might be: 55 | 56 | !(collateralised=TRUE) 57 | 58 | The second difference is that AuthorisationToUse has no actions. For example, it can't be traded. When the user Bob has an authorisation, he didn't see *authorisation* token next to the car token, but just the CarToken, with its permitted actions listed. If Alice sends Bob a second authorisation, maybe because the first is expiring, Bob doesn't see two distinct *AuthorisationToken* each with a *CarToken* in it; instead, Bob sees just one Token, with expiry extended. 59 | 60 | An authorisation would be a token, if it represents a lease. This is the case when the authorisation is obtained through a transaction, that is, Bob paid to obtain a lease of the car. The lease might even be transferrable. 61 | 62 | But not every object derived from a CarToken can be a token. Transfer of ownership, for example, is an event. An event is an artifact too: 63 | 64 | { 65 | "owner": 0xdecafbad, 66 | "newOnwer": 0xbaddecaf, 67 | "EventBlockId": 1234546, # this is an Operational Attribute, not declared in the event's ABI 68 | "EventTransactionId", 0x83480327027503...03082FF # also an Operational Attribute 69 | } 70 | 71 | Even if the Transfer of ownership event has a 1-to-1 relationship with another token, e.g. *StampDuty* token, and that the event's properties coincide with the attributes of that token, the event is still an artifact and the StampDuty still a token, not the same thing. The event data-object only exists in the scope of the token, while the *StampDuty* token has its own scope. (It also does not *have to* inherit the event's properties). This relationship is similar to how a SalesOffer data-object, which has price information, from the view of blockchain, has a 1-to-1 relationship with the SalesContract token, which is created by the buyer sending a transaction with SalesOffer data-object as payload, and every *SalesOffer* data-object property becomes a *SalesContract* token attribute, they are still two different thing. 72 | 73 | Conclusion: 74 | ![Venn Diagram of Artifacts and Tokens](img/token_data.svg) 75 | 76 | # Filters 77 | 78 | Filters are used to selecting data-objects and tokens. In the above examples, we used filters a few times: 79 | 80 | rego=4JHLC 81 | 82 | VIN=KL3TA48E9EB541191 83 | 84 | !(collateralised=TRUE) 85 | 86 | Unlike structured query language like SQL, a filter does not specify what to do when the filtering is done. For example, when an attribute sources data from an event origin: 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | - the filter `label=${tokenID}` selects data-objects. In this case, only one is expected to be sifted through. 95 | - should there be more than one, name will be a multi-valued (ordered) attribute, like `allowed` shown before. 96 | - find the specified property, in this case `name`, whose value is used for the value of the attribute. 97 | 98 | As you can see, in TokneScript, a filter can be used on events (to filter events), or it can be used as the condition for actions. Or, it can be used as values of properties, in which case it either expresses a search or specifies which token an data-object (signed message or event) is about. By today's NFT convention, you can imagine something like this 99 | 100 | tokenID=0xca6abbe9d7f11422cb6ca7629fbf6fe9efb1c621f71ce8f02b9f2a230097404f 101 | 102 | being the most frequently used filter when it is used as a property (e.g. when someone floats an order to sell a kitty by tokenID). 103 | 104 | ## filtering multi-value attributes 105 | 106 | In TokenScript filter we do not distinguish between the two forms of multi-value expressions (observe the last property): 107 | 108 | { 109 | "objectClass": "AuthorisationToUse", 110 | "car": "rego=4JHLC", 111 | "user": "Bob", 112 | "start": 2019-09-09, 113 | "end": 2019-09-10, 114 | "allow": ["drive", "refuel", "service", "park", "locate"] 115 | } 116 | 117 | and 118 | 119 | { 120 | "objectClass": "AuthorisationToUse", 121 | "car": "rego=4JHLC", 122 | "user": "Bob", 123 | "start": 2019-09-09, 124 | "end": 2019-09-10, 125 | "allow": "drive", 126 | "allow": "refuel", 127 | "allow": "service", 128 | "allow": "park", 129 | "allow": "locate" 130 | } 131 | 132 | For example, this filter matches the data-object: 133 | 134 | allowedAction=drive 135 | 136 | The following filter also matches the data-object: 137 | 138 | (&(allowedAction=drive)(allowedAction=refuel)) 139 | 140 | The distinction between a set and an ordered list is incomprehensible to millennium developers and potentially create issues with signed data. Recognising that, multi-valued attributes are always ordered-multi-valued attributes. 141 | 142 | Notice that filters use Polish notation to be parsed into abstract syntax tree with less gas. When data-objects are in the payload, the smart contract has to go through them with either pre-compiled filter or take the filter from another data-object or token attribute value. 143 | -------------------------------------------------------------------------------- /doc/getting_started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | The best way to get started is to run through the [tutorial](https://github.com/AlphaWallet/TokenScript-Examples/tree/master/tutorial). 4 | 5 | ## Implicit Attributes 6 | 7 | There are a few implicit attributes available: 8 | 9 | * name 10 | * symbol 11 | * contractAddress 12 | * ownerAddress 13 | * tokenId 14 | 15 | `tokenId` is especially useful for defining attributes with a function origin like this: 16 | 17 | ``` 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ``` 28 | 29 | This is written with the assumption that the `getLocality()` smart contract function expects a single `uint256` argument representing the tokenId. 30 | 31 | Here's another example with [ERC721](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md)'s `ownerOf(uint256)`: 32 | 33 | ``` 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | ``` 44 | -------------------------------------------------------------------------------- /doc/img/airbnb.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartTokenLabs/TokenScript/0581e9c0448483c71555139c537cde9c1e99ac28/doc/img/airbnb.jpeg -------------------------------------------------------------------------------- /doc/img/iconified-view.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartTokenLabs/TokenScript/0581e9c0448483c71555139c537cde9c1e99ac28/doc/img/iconified-view.jpeg -------------------------------------------------------------------------------- /doc/img/readme/share_facebook-btn.svg: -------------------------------------------------------------------------------- 1 | share -------------------------------------------------------------------------------- /doc/img/readme/share_linkedin-btn.svg: -------------------------------------------------------------------------------- 1 | share -------------------------------------------------------------------------------- /doc/img/readme/share_mail-btn.svg: -------------------------------------------------------------------------------- 1 | share -------------------------------------------------------------------------------- /doc/img/readme/share_reddit-btn.svg: -------------------------------------------------------------------------------- 1 | share -------------------------------------------------------------------------------- /doc/img/readme/share_telegram-btn.svg: -------------------------------------------------------------------------------- 1 | share -------------------------------------------------------------------------------- /doc/img/readme/share_tweet-btn.svg: -------------------------------------------------------------------------------- 1 | share -------------------------------------------------------------------------------- /doc/img/readme/tokenscript-stack.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartTokenLabs/TokenScript/0581e9c0448483c71555139c537cde9c1e99ac28/doc/img/readme/tokenscript-stack.jpg -------------------------------------------------------------------------------- /doc/img/regular-view.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartTokenLabs/TokenScript/0581e9c0448483c71555139c537cde9c1e99ac28/doc/img/regular-view.jpeg -------------------------------------------------------------------------------- /doc/img/token_data.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 67 | SignedData 83 | 90 | Blockchain Truth 101 | 108 | 115 | EventData 131 | Return value / structof pure functions 147 | 154 | Token 165 | 168 | 175 | Attestation 186 | 193 | BlockchainToken 209 | 214 | VirtualToken 230 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /doc/javascript_api.md: -------------------------------------------------------------------------------- 1 | # API 2 | 3 | ## Intro 4 | 5 | ### Who provides the API 6 | 7 | TokenScript supporting user-agent (Dapp browser), wallet or self-supporting Dapps themselves provides JavaScript API. 8 | 9 | ### For whom to use 10 | 11 | - The JavaScript running in TokenScript 12 | - Javascript running in Dapp websites. 13 | 14 | Both uses the same JavaScript API but with nuances. For example: 15 | 16 | --- 17 | 18 | Difference in the tokens available to be accessed through this API. 19 | 20 | - The tokens available to a Dapp website depends on what token the user has chosen to use on that Dapp, as well as the tokens not owned by the current user, like the token the Dapp offers to transfer to the user (e.g. auction Dapp website) or to be created for the user (e.g. purchasing a new tokenised FIFA Ticket). 21 | 22 | - The tokens available to the Javascript in a TokenScript is typically the current token itself and dependencies. In the TokenView, may also include other tokens that can interact with the current token. 23 | 24 | --- 25 | 26 | The API has 2 parts 27 | 28 | A. The token data. 29 | 30 | B. The callback when the token data changes. 31 | 32 | ## A. Token Data 33 | 34 | --- 35 | The shape of the data is: 36 | 37 | ``` 38 | web3.tokens = [ 39 | all: [ 40 | token, 41 | token, 42 | ... 43 | ], 44 | definition: { 45 | "0xD8e5F58DE3933E1E35f9c65eb72cb188674624F3": tokenDefinition, 46 | "0xf018225735f70a1961B6B8aa07B005e6392072E7": tokenDefinition, 47 | ... 48 | }, 49 | dataChanged: function(oldTokens, updatedTokens, tokenCardId) 50 | ] 51 | ``` 52 | 53 | For a fungible token, like airline point, the token variable is like this: 54 | 55 | ``` 56 | token = { 57 | contractAddress: "0xD8e5F58DE3933E1E35f9c65eb72cb188674624F3", 58 | balance: 98.66250478, 59 | available: 96.6625047, 60 | locked: {"state-channel-0934": 2}, 61 | votingRights: 73.929672, 62 | transferrable: 54.6625047, 63 | expiry: {"2018-09-03": 34, "2018-18-04": 10, "never": 54.6625047}, 64 | } 65 | ``` 66 | 67 | Where attributes like `votingRights` are defined in TokenScript. 68 | 69 | For a non-fungible token, like a ticket to an event: 70 | 71 | ``` 72 | token = { 73 | contractAddress: "0xD8e5F58DE3933E1E35f9c65eb72cb188674624F3", 74 | instances: [ 75 | instance, 76 | instance, 77 | ... 78 | ] 79 | } 80 | 81 | instance = { 82 | _count: 1, 83 | contractAddress: "0xD8e5F58DE3933E1E35f9c65eb72cb188674624F3", 84 | numero: 11, # sequence of the ticket 85 | section: "22", 86 | building: "Some building", 87 | street: "Some street", 88 | country: "SG", 89 | ... 90 | } 91 | 92 | tokenDefinition = { 93 | attributeType: { 94 | attributeId: attribute, 95 | category: { 96 | name: "Cat", 97 | syntax: "Integer", 98 | single: "true", 99 | }, 100 | startTime: { 101 | name: "Event Start Time", 102 | syntax: "BinaryTime", 103 | }, 104 | ... 105 | }, 106 | grouping: {...}, 107 | ordering: {...}, 108 | symbol: "TICKET", 109 | name: "Ticket Token" 110 | }, 111 | 112 | attribute = { 113 | name: "Cat", 114 | value: "TEST" 115 | } 116 | 117 | ``` 118 | 119 | The metadata of a token is available in `web3.tokens.definition` with the contract address in [EIP55](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md) as the key. The metadata is basically a 1:1 map from the relevant parts of the asset definition. Only the localized attribute names (and not grouping, ordering) is available for now. 120 | 121 | `attributeType` contains type information of each attribute, e.g. its name, syntax or whether the attribute type is single-valued. For example, to access the localized attribute name `"countryA"` for the token with contract `"0xD8e5F58DE3933E1E35f9c65eb72cb188674624F3"` use: 122 | 123 | ``` 124 | web3.tokens.definition["0xD8e5F58DE3933E1E35f9c65eb72cb188674624F3"].attributeType["countryA"]["name"] 125 | ``` 126 | 127 | All attribute types are in the `attributeType` dictionary, even for attributes that are not used in any of the token (for fungible) or tokenInstance (for non-fungible). For example, a user might have a few EventTicket tokens, none of them have `category` attribute because the event doesn't categorise tickets, but `category` can be found in `attributeType` dictionary because it's typed. This is needed in many cases, e.g. when TokenScript is going to create a new ticket with `category` attribute. 128 | 129 | The other elements in a `tokenDefinition`, like `name` and `symbol`, refer to the name and symbol of the token. 130 | 131 | *Only* `web3.tokens.dataChanged` (and not `web3.tokens.all` nor `definition`) is available in AlphaWallet's Android and iOS build at the moment. 132 | 133 | ### Attribute-Value Pair? Not always 134 | 135 | In the previous example, observe that not every attribute of a token is of a primitive type. 136 | 137 | 138 | ``` 139 | token = { 140 | contractAddress: "0xD8e5F58DE3933E1E35f9c65eb72cb188674624F3", 141 | balance: 98.66250478, 142 | available: 96.6625047, 143 | locked: {"state-channel-0934": 2}, 144 | votingRights: 73.929672, 145 | transferrable: 54.6625047, 146 | expiry: {"2018-09-03": 34, "2018-18-04": 10, "never": 54.6625047}, 147 | } 148 | ``` 149 | 150 | There are two attributes that are not of a primitive type: `locked`, which represents the balance committed somehow (typically, to a state channel), and `expiry` which is how much the balance will disappear at certain date, typically used as an incentive for users to spend, the opposite incentive of Bitcoin. 151 | 152 | What form of value do we have for each attribute is a result of the attribute-type found in `tokenDefinition`. 153 | 154 | Take time as an example. Typically, blockchain uses `BinaryTime` syntax for gas efficiency, which is just binary encoded UnixTime. When used as the time of an event, there is no ambiguity which point of time it refers to, no matter in which timezone the event happens. In such case, the attribute is a dictionary of a single key: 155 | 156 | ``` 157 | instance = { 158 | section: "22", 159 | startTime: { 160 | date: new Date("1985-11-07T02:06:27") 161 | } 162 | ... 163 | } 164 | ``` 165 | 166 | Date object is represented by the code with which it would have been created. The key is `date` to inherit the misnaming by JavaScript. 167 | 168 | However, in the case the time relevant to the timezone is important, the token designer would have supplied a [GeneralizedTime](https://en.wikipedia.org/wiki/GeneralizedTime). Take a FIFA football match ticket as an example, `matchTime` attribute is a dictionary of two keys: `date` as a Date object, not containing timezone, and the raw value for GeneralizedTime which has the timezone in it. 169 | 170 | ``` 171 | instance = { 172 | section: "22", 173 | matchTime: { 174 | generalizedTime: "19851106210627-0500", 175 | date: new Date("1985-11-07T02:06:27") 176 | } 177 | ... 178 | } 179 | ``` 180 | 181 | If a developer intends to find out if an attribute is of `BinaryTime` or `GeneralizedTime`, he can look up the definition (search for `BinaryTime` in the beginning of this document for an example). 182 | 183 | The value of `matchTime.date` would be a normal `Date` object, the time the match starts. As every `Date` object, it doesn't contain the timezone information. But if you want to display a venue-specific time, eg. a soccer game match time at the venue, you need to extract that information from the `generalizedTime` string, like shown in the [example](../examples/ticket/js/generalized-time-test.html). 184 | 185 | ## B. Callback 186 | 187 | TokenScript's JavaScript API is designed to be asynchronous. 188 | 189 | --- 190 | 191 | There are 3 args supplied to the `web3.tokens.dataChanged` callback: 192 | 193 | * old data — this contains the data — token instance and card attribute values — before the change 194 | * updated data — this contains the data — token instance and card attribute values — after the change 195 | * tokenCardId — the CSS ID of the wrapper `
` to place content in 196 | 197 | Developers can use this callback and the arguments to figure out what has changed. We might point them to a good JSON-diff library; but since the purpose here is just to render a static layout, they can just re-render the whole DOM as we do in our examples. 198 | 199 | A simple way to implement that would be: 200 | 201 | ``` 202 | web3.tokens.dataChanged = (old, updated, tokenCardId) => { 203 | const data = updated 204 | document.getElementById(tokenCardId).getElementsByClassName("contents")[0].innerHTML = new Token(data).render() 205 | } 206 | ``` 207 | 208 | TokenScript will automatically generate a `
` with a unique ID (`tokenCardId`) and wrap the TokenScript view with it. This wrapper `
` will have the CSS class `".token-card` so it can be styled (if desired). 209 | 210 | The snippet assumes the presence of a `
` that has the class name `contents`. Adding this `
` makes it easier to access/replace the contents. Note that developers should *not* do this as the wrapper `
` generated by TokenScript wraps around HTML, CSS and JavaScript provided by the TokenScript view: 211 | 212 | ``` 213 | document.getElementById(tokenCardId).innerHTML = replacementDomHtml 214 | ``` 215 | 216 | The above-mentioned `Token.render()` function generates a DOM from a dictionary of token attributes. 217 | 218 | Within the `Token` class, the token data can be access like this: 219 | 220 | ``` 221 | const tokenAttributeValue = this.props.token.tokenAttribute1 222 | ``` 223 | 224 | and the card data like this: 225 | 226 | ``` 227 | const cardAttributeValue = this.props.card.tokenAttribute1 228 | ``` 229 | 230 | And if we are sure the token and card attribute names don't clash, we can pretend they are in the same namespace with: 231 | 232 | ``` 233 | web3.tokens.dataChanged = (old, updated, tokenCardId) => { 234 | const data = Object.assign({}, updated.token, updated.card) 235 | document.getElementById(tokenCardId).getElementsByClassName("contents")[0].innerHTML = new Token(data).render() 236 | } 237 | ``` 238 | 239 | Then this is unchanged: 240 | 241 | ``` 242 | const attributeValue = this.props.tokenAttribute1 //or this.props.cardAttribute1 243 | ``` 244 | 245 | Future 246 | --- 247 | In (A), we can also stuff the entire list of tokens in the user's Ethereum wallet in there (in a future iteration) under the `all` key. We might have to key them by wallet/networks too. Performance is a concern, but this simple approach has quite a number of advantages. Perhaps it can be partially mitigated by adding a permission call that TokenScript developers have to make to make `tokens` accessible, maybe as part of the permission granted via https://eips.ethereum.org/EIPS/eip-1102 (which we should implement anyway) or a new function call. 248 | 249 | The development and debugging experience is a little tedious. With access to the simulator, we can drop updated files and run a web inspector on the simulator's TokenScript webview to look at the console.log output. Without the app's source, we can AirDrop TokenScript files to the app. The developer experience is something we need to look into a bit more. 250 | 251 | Implementing the TokenScript API and Rendering in the TokenScript Clients 252 | -------------------------------------------------------------------------------- /doc/negotiator.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SmartTokenLabs/TokenScript/0581e9c0448483c71555139c537cde9c1e99ac28/doc/negotiator.dia -------------------------------------------------------------------------------- /funding.json: -------------------------------------------------------------------------------- 1 | { 2 | "opRetro": { 3 | "projectId": "0x471f2d9cc373f1c6e025c283a41964440c09bf773c9427c62622948e92620fa0" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /schema/README.md: -------------------------------------------------------------------------------- 1 | # TokenScript Schema 2 | 3 | TokenScript schema serves to check if your TokenScript is with the correct syntax. 4 | 5 | This namespace identifies the current schema: 6 | 7 | http://tokenscript.org/2020/06/tokenscript 8 | 9 | When there is a minor change with the addition of new features and elements, we update the schema without invalidating the old TokenScripts that are already signed and put to use. The namespace identifier remains. This process is slow and normally taking months or years. 10 | 11 | But every few cycles, evolution leaps forward, and we resort to declaring a new namespace with older elements obsoleted and usage-changed. 12 | 13 | The last time it happened was in Jun 2020. 14 | 15 | When the namespace change, existing Tokenscripts has to be migrated and re-signed. We will be providing migration tools should that happen again. 16 | 17 | 18 | ### The schema namespace in this repo is above my version; how can I use an older version? 19 | 20 | If you have a TokenScript file in an older namespace, they can still be checked for validity, except that they will not run on the latest TokenScript engine implementations unless they explicitly supports outdated TokenScript formats. 21 | 22 | If you are the author of such TokenScripts, you should migrate to the current TokenScript namespace. We promise that we make such a change as infrequent as possible. 23 | 24 | Example: `http://tokenscript.org/2019/10/tokenscript` was the prior namespace. Old TokenScripts under that namespace can still be found, for example, here: 25 | 26 | https://repo.tokenscript.org/aw.app/2019/10/ 27 | 28 | To make sure the users who didn't upgrade their AlphaWallet can still use the tokens the old way. But they will no longer be updated to include new functions. 29 | -------------------------------------------------------------------------------- /schema/asnx.xsd: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /schema/data.xsd: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /schema/ethereum.xsd: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | -------------------------------------------------------------------------------- /schema/xml.xsd: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 |
10 |

About the XML namespace

11 | 12 |
13 |

14 | This schema document describes the XML namespace, in a form 15 | suitable for import by other schema documents. 16 |

17 |

18 | See 19 | http://www.w3.org/XML/1998/namespace.html and 20 | 21 | http://www.w3.org/TR/REC-xml for information 22 | about this namespace. 23 |

24 |

25 | Note that local names in this namespace are intended to be 26 | defined only by the World Wide Web Consortium or its subgroups. 27 | The names currently defined in this namespace are listed below. 28 | They should not be used with conflicting semantics by any Working 29 | Group, specification, or document instance. 30 |

31 |

32 | See further below in this document for more information about how to refer to this schema document from your own 34 | XSD schema documents and about the 35 | namespace-versioning policy governing this schema document. 36 |

37 |
38 |
39 |
40 |
41 | 42 | 43 | 44 | 45 |
46 | 47 |

lang (as an attribute name)

48 |

49 | denotes an attribute whose value 50 | is a language code for the natural language of the content of 51 | any element; its value is inherited. This name is reserved 52 | by virtue of its definition in the XML specification.

53 | 54 |
55 |
56 |

Notes

57 |

58 | Attempting to install the relevant ISO 2- and 3-letter 59 | codes as the enumerated possible values is probably never 60 | going to be a realistic possibility. 61 |

62 |

63 | See BCP 47 at 64 | http://www.rfc-editor.org/rfc/bcp/bcp47.txt 65 | and the IANA language subtag registry at 66 | 67 | http://www.iana.org/assignments/language-subtag-registry 68 | for further information. 69 |

70 |

71 | The union allows for the 'un-declaration' of xml:lang with 72 | the empty string. 73 |

74 |
75 |
76 |
77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 |
87 | 88 | 89 | 90 | 91 |
92 | 93 |

space (as an attribute name)

94 |

95 | denotes an attribute whose 96 | value is a keyword indicating what whitespace processing 97 | discipline is intended for the content of the element; its 98 | value is inherited. This name is reserved by virtue of its 99 | definition in the XML specification.

100 | 101 |
102 |
103 |
104 | 105 | 106 | 107 | 108 | 109 | 110 |
111 | 112 | 113 | 114 |
115 | 116 |

base (as an attribute name)

117 |

118 | denotes an attribute whose value 119 | provides a URI to be used as the base for interpreting any 120 | relative URIs in the scope of the element on which it 121 | appears; its value is inherited. This name is reserved 122 | by virtue of its definition in the XML Base specification.

123 | 124 |

125 | See http://www.w3.org/TR/xmlbase/ 127 | for information about this attribute. 128 |

129 |
130 |
131 |
132 |
133 | 134 | 135 | 136 | 137 |
138 | 139 |

id (as an attribute name)

140 |

141 | denotes an attribute whose value 142 | should be interpreted as if declared to be of type ID. 143 | This name is reserved by virtue of its definition in the 144 | xml:id specification.

145 | 146 |

147 | See http://www.w3.org/TR/xml-id/ 149 | for information about this attribute. 150 |

151 |
152 |
153 |
154 |
155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 |
166 | 167 |

Father (in any context at all)

168 | 169 |
170 |

171 | denotes Jon Bosak, the chair of 172 | the original XML Working Group. This name is reserved by 173 | the following decision of the W3C XML Plenary and 174 | XML Coordination groups: 175 |

176 |
177 |

178 | In appreciation for his vision, leadership and 179 | dedication the W3C XML Plenary on this 10th day of 180 | February, 2000, reserves for Jon Bosak in perpetuity 181 | the XML name "xml:Father". 182 |

183 |
184 |
185 |
186 |
187 |
188 | 189 | 190 | 191 |
192 |

About this schema document

193 | 194 |
195 |

196 | This schema defines attributes and an attribute group suitable 197 | for use by schemas wishing to allow xml:base, 198 | xml:lang, xml:space or 199 | xml:id attributes on elements they define. 200 |

201 |

202 | To enable this, such a schema must import this schema for 203 | the XML namespace, e.g. as follows: 204 |

205 |
206 |           <schema . . .>
207 |            . . .
208 |            <import namespace="http://www.w3.org/XML/1998/namespace"
209 |                       schemaLocation="http://www.w3.org/2001/xml.xsd"/>
210 |      
211 |

212 | or 213 |

214 |
215 |            <import namespace="http://www.w3.org/XML/1998/namespace"
216 |                       schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
217 |      
218 |

219 | Subsequently, qualified reference to any of the attributes or the 220 | group defined below will have the desired effect, e.g. 221 |

222 |
223 |           <type . . .>
224 |            . . .
225 |            <attributeGroup ref="xml:specialAttrs"/>
226 |      
227 |

228 | will define a type which will schema-validate an instance element 229 | with any of those attributes. 230 |

231 |
232 |
233 |
234 |
235 | 236 | 237 | 238 |
239 |

Versioning policy for this schema document

240 |
241 |

242 | In keeping with the XML Schema WG's standard versioning 243 | policy, this schema document will persist at 244 | 245 | http://www.w3.org/2009/01/xml.xsd. 246 |

247 |

248 | At the date of issue it can also be found at 249 | 250 | http://www.w3.org/2001/xml.xsd. 251 |

252 |

253 | The schema document at that URI may however change in the future, 254 | in order to remain compatible with the latest version of XML 255 | Schema itself, or with the XML namespace itself. In other words, 256 | if the XML Schema or XML namespaces change, the version of this 257 | document at 258 | http://www.w3.org/2001/xml.xsd 259 | 260 | will change accordingly; the version at 261 | 262 | http://www.w3.org/2009/01/xml.xsd 263 | 264 | will not change. 265 |

266 |

267 | Previous dated (and unchanging) versions of this schema 268 | document are at: 269 |

270 | 280 |
281 |
282 |
283 |
284 | 285 |
286 | 287 | -------------------------------------------------------------------------------- /schema/xmldsig-core-schema.xsd: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | ]> 11 | 12 | 27 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 216 | 217 | 218 | 219 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | -------------------------------------------------------------------------------- /xmldsig/README.md: -------------------------------------------------------------------------------- 1 | # Demonstrate how to verify XMLDSIG signature 2 | 3 | This repository shows how to verify XMLDSig signature, starting with JavaScript and hope to achieve most programming languages. 4 | 5 | Its use is not limited to TokenScript since we are talking about standard XMLDSIG. 6 | 7 | ## Usage 8 | 9 | In all following examples, assume you have cloned [TokenScript-Repo](https://github.com/AlphaWallet/TokenScript-Repo) - a nop-exhaustive TokenScript collection - into your home directory (`~/TokenScript-Repo`) for testing purpose. 10 | 11 | Each directory (language version) has a README file. Please check it there for further instructions. 12 | -------------------------------------------------------------------------------- /xmldsig/js/README.md: -------------------------------------------------------------------------------- 1 | # Demonstrating verifying XML Digital Signature (xmldsig) # 2 | 3 | xmldsig is used to sign every TokenScript file. This directory contains a 4 | demonstration on how to verify xmldsig with JavaScript code. 5 | 6 | ## Prepare ## 7 | 8 | First, make sure your nodejs is above version 15.0.0 where [WebCrypto was introduced](https://www.nearform.com/blog/implementing-the-web-cryptography-api-for-node-js-core/) 9 | 10 | Then, install the dependencies in the `src` directory: 11 | 12 | ```` 13 | src$ npm install 14 | ```` 15 | 16 | Finally, also in `src`, apply an xmldom patch for [a known bug](https://github.com/xmldom/xmldom/issues/203). Note that this patch is destructive, the resulting xmldom won't work properly on html files (which we don't use in our case). Such a patch will not be needed (hence will be deleted from this repo) when xmldom release the next version after 0.5.0. 17 | 18 | ```` 19 | src$ patch -p0 < xmldom.patch 20 | ```` 21 | 22 | ## Test ## 23 | 24 | Let's say you have a bunch of signed TokenScripts residing in `../../../TokenScript-Repo/aw.app/2020/06/` (which you can get by checking out [TokenScript-Repo](https://github.com/AlphaWallet/TokenScript-Repo)). Run the files through the `xmldsigverifier.js` script: 25 | 26 | ```` 27 | src$ node xmldsigverifier.js ../../../TokenScript-Repo/aw.app/2020/06/* 28 | [ OK ] ../../../TokenScript-Repo/aw.app/2020/06/aDAI.tsml 29 | [ OK ] ../../../TokenScript-Repo/aw.app/2020/06/cBAT.tsml 30 | … 31 | [ OK ] ../../../TokenScript-Repo/aw.app/2020/06/WETH.tsml 32 | ```` 33 | 34 | ## Creating a webpack ## 35 | 36 | Somehow webpack version >5 requires further configuration which hasn't been done, so make sure you use an earlier version of webpack, such as 4.44.2. Run `webpack` at this level, that is, not under `src` directory: 37 | 38 | 39 | ```` 40 | $ webpack 41 | Hash: 5f6510c90828ac124c4f 42 | Version: webpack 4.44.2 43 | Time: 8110ms 44 | Built at: 08/04/2021 11:31:47 45 | Asset Size Chunks Chunk Names 46 | main.js 1.1 MiB 0 [emitted] [big] main 47 | Entrypoint main [big] = main.js 48 | ```` 49 | 50 | The resulting file should be `dist/main.js`. 51 | 52 | ## Work in progress ## 53 | 54 | - It currently fails with ECDSA signatures. 55 | 56 | - Because this signature verifier doesn't check the certificates, the result is not an indication whether or not the TokenScript is signed with the correct key. More work needs to be done to use it in production environment. 57 | -------------------------------------------------------------------------------- /xmldsig/js/src/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | //const crypto = require('crypto').webcrypto; 4 | import crypto from 'crypto'; 5 | import XmlDSigJs from "xmldsigjs"; 6 | XmlDSigJs.Application.setEngine("WebCrypto", crypto.webcrypto); 7 | 8 | export default function verify(xml_string) { 9 | let doc = XmlDSigJs.Parse(xml_string); 10 | let signature = doc.getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "Signature"); 11 | 12 | let signedXml = new XmlDSigJs.SignedXml(doc); 13 | signedXml.LoadXml(signature[0]); 14 | 15 | return signedXml.Verify() 16 | } 17 | 18 | -------------------------------------------------------------------------------- /xmldsig/js/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "engines": { 4 | "node": ">=0.15" 5 | }, 6 | "engineStric": true, 7 | "dependencies": { 8 | "xmldsigjs": "^2.1.3" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /xmldsig/js/src/xmldom.patch: -------------------------------------------------------------------------------- 1 | diff -ru node_modules/xmldom.old/lib/dom.js node_modules/xmldom/lib/dom.js 2 | --- node_modules/xmldom.old/lib/dom.js 1985-10-26 18:15:00.000000000 +1000 3 | +++ node_modules/xmldom/lib/dom.js 2021-04-07 17:52:51.494473360 +1000 4 | @@ -23,7 +23,7 @@ 5 | pt.constructor = Class 6 | } 7 | } 8 | -var htmlns = 'http://www.w3.org/1999/xhtml' ; 9 | +var htmlns = 'http://www.w3.org/1999/html' ; 10 | // Node Types 11 | var NodeType = {} 12 | var ELEMENT_NODE = NodeType.ELEMENT_NODE = 1; 13 | diff -ru node_modules/xmldom.old/lib/sax.js node_modules/xmldom/lib/sax.js 14 | --- node_modules/xmldom.old/lib/sax.js 1985-10-26 18:15:00.000000000 +1000 15 | +++ node_modules/xmldom/lib/sax.js 2021-04-07 17:53:56.875198887 +1000 16 | @@ -195,7 +195,7 @@ 17 | 18 | 19 | 20 | - if(el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed){ 21 | + if(el.uri === 'http://www.w3.org/1999/html' && !el.closed){ 22 | end = parseHtmlSpecialContent(source,end,el.tagName,entityReplacer,domBuilder) 23 | }else{ 24 | end++; 25 | @@ -333,7 +333,7 @@ 26 | errorHandler.warning('attribute "'+value+'" missed quot(")!'); 27 | addAttribute(attrName, value.replace(/&#?\w+;/g,entityReplacer), start) 28 | }else{ 29 | - if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)){ 30 | + if(currentNSMap[''] !== 'http://www.w3.org/1999/html' || !value.match(/^(?:disabled|checked|selected)$/i)){ 31 | errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!') 32 | } 33 | addAttribute(value, value, start) 34 | @@ -381,7 +381,7 @@ 35 | //case S_ATTR_NOQUOT_VALUE:void();break; 36 | case S_ATTR_SPACE: 37 | var tagName = el.tagName; 38 | - if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)){ 39 | + if(currentNSMap[''] !== 'http://www.w3.org/1999/html' || !attrName.match(/^(?:disabled|checked|selected)$/i)){ 40 | errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!') 41 | } 42 | addAttribute(attrName, attrName, start); 43 | -------------------------------------------------------------------------------- /xmldsig/js/src/xmldsigverifier.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import fs from 'fs'; 4 | import verify from './index.js'; 5 | 6 | if (process.argv.length == 2) { 7 | console.log("xmldsigverifier demonstration script. Need parameters - xml files with enveloped signatures") 8 | } 9 | 10 | for (var i=2; i { 14 | if (res == true) { 15 | console.log("[ OK ] " + filename); 16 | } else { 17 | console.log("[FAILED] " + filename); 18 | } 19 | }) 20 | .catch(e => { 21 | console.log("[ERROR ] " + filename); 22 | console.log(e) 23 | }); 24 | } 25 | 26 | --------------------------------------------------------------------------------