Skip to content

Commit e669013

Browse files
committed
implemented counterparty CIP2 payment URI handler
1 parent 05b70e3 commit e669013

File tree

10 files changed

+140
-24
lines changed

10 files changed

+140
-24
lines changed

config-templates/.desktop

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ Terminal=false
99
Categories=Finance;Internet;
1010
X-Ubuntu-Touch=true
1111
X-Ubuntu-StageHint=SideStage
12-
MimeType=application/pockets;x-scheme-handler/pockets;application/bitcoin;x-scheme-handler/bitcoin;application/copay;x-scheme-handler/copay;
12+
MimeType=application/pockets;x-scheme-handler/pockets;application/bitcoin;x-scheme-handler/bitcoin;application/copay;x-scheme-handler/copay;application/counterparty;x-scheme-handler/counterparty;
1313

1414

config-templates/setup-win.iss

+5
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,8 @@ Root: HKCR; Subkey: "pockets"; ValueType: "string"; ValueName: "URL Protocol"; V
6161
Root: HKCR; Subkey: "pockets\DefaultIcon"; ValueType: "string"; ValueData: "{app}\{#MyAppExeName},0"
6262
Root: HKCR; Subkey: "pockets\shell\open\command"; ValueType: "string"; ValueData: """{app}\{#MyAppExeName}"" ""%1"""
6363

64+
Root: HKCR; Subkey: "counterparty"; ValueType: "string"; ValueData: "URL:Counterparty Custom Protocol"; Flags: uninsdeletekey
65+
Root: HKCR; Subkey: "counterparty"; ValueType: "string"; ValueName: "URL Protocol"; ValueData: ""
66+
Root: HKCR; Subkey: "counterparty\DefaultIcon"; ValueType: "string"; ValueData: "{app}\{#MyAppExeName},0"
67+
Root: HKCR; Subkey: "counterparty\shell\open\command"; ValueType: "string"; ValueData: """{app}\{#MyAppExeName}"" ""%1"""
68+

extra/linux/Install.sh

+1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ sudo ln -s -f "$thisdir"/.desktop ~/.local/share/applications/pockets.desktop
55
xdg-mime default pockets.desktop x-scheme-handler/pockets
66
xdg-mime default pockets.desktop x-scheme-handler/copay
77
xdg-mime default pockets.desktop x-scheme-handler/bitcoin
8+
xdg-mime default pockets.desktop x-scheme-handler/counterparty
89
echo "Pockets wallet installed!"

extra/osx/Info.plist

+9-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,15 @@
7676
<array>
7777
<string>pockets</string>
7878
</array>
79-
</dict>
79+
</dict>
80+
<dict>
81+
<key>CFBundleURLName</key>
82+
<string>Counterparty URL</string>
83+
<key>CFBundleURLSchemes</key>
84+
<array>
85+
<string>counterparty</string>
86+
</array>
87+
</dict>
8088
</array>
8189
<key>CFBundleVersion</key>
8290
<string>2.4.2</string>

public/views/paymentUri.html

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ <h1 translate>Make a payment to</h1>
1414
<div class="panel size-14">
1515
<div class="ellipsis"><b translate>Address</b>: {{payment.uri.address.toString()}}</div>
1616
<div ng-show="payment.uri.amount"><b translate>Amount</b>: {{payment.uri.amount}}</div>
17+
<div ng-show="payment.uri.label"><b translate>Label</b>: {{payment.uri.label}}</div>
1718
<div ng-show="payment.uri.message"><b translate>Message</b>: {{payment.uri.message}}</div>
1819
<div ng-show="payment.uri.network == 'testnet'"><b translate>Network</b>: {{payment.uri.network}}</div>
1920
</div>

public/views/walletHome.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -369,15 +369,15 @@ <h4 class="title m0" ng-show="!index.updating">
369369
<div class="row m20t">
370370
<div class="large-12 large-centered columns">
371371
<form name="sendForm" novalidate>
372-
372+
<h2 class="send-label" ng-if="home.send_label">{{ home.send_label }}</h2>
373373
<!-- token -->
374374
<div>
375375
<div class="row collapse">
376376
<label for="token" class="left" >
377377
<span translate>Token</span>
378378
</label>
379379
<div class="input">
380-
<select class="m10t" ng-model="_token"
380+
<select class="m10t" ng-model="_token" ng-disabled="home.lockSendToken"
381381
ng-options="tokenValue as tokenLabel for (tokenValue, tokenLabel) in home.getSendableTokens()"
382382
ng-change="home.setTokenName(_token)">
383383
</select>

src/js/controllers/paymentUri.js

+52-9
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,79 @@
11
'use strict';
22
angular.module('copayApp.controllers').controller('paymentUriController',
3-
function($rootScope, $scope, $stateParams, $location, $timeout, profileService, configService, lodash, bitcore, go) {
3+
function($rootScope, $scope, $stateParams, $location, $timeout, profileService, configService, lodash, bitcore, go, bvamService) {
44
function strip(number) {
55
return (parseFloat(number.toPrecision(12)));
66
};
7-
7+
8+
function delimitNumber(n) {
9+
return (n + "").replace(/\b(\d+)((\.\d+)*)\b/g, function(a, b, c) {
10+
return (b.charAt(0) > 0 && !(c || ".").lastIndexOf(".") ? b.replace(/(\d)(?=(\d{3})+$)/g, "$1,") : b) + c;
11+
});
12+
};
13+
14+
var self = this;
15+
self.bvamData = {};
16+
self.tokens = [];
17+
818
// Build bitcoinURI with querystring
919
this.init = function() {
1020
var query = [];
11-
this.bitcoinURI = $stateParams.url;
12-
21+
this.origURI = $stateParams.url;
22+
this.isCounterparty = $stateParams.url.includes('counterparty:');
23+
this.bitcoinURI = $stateParams.url.replace('counterparty:', 'bitcoin:');
1324
var URI = bitcore.URI;
14-
var isUriValid = URI.isValid(this.bitcoinURI);
15-
if (!URI.isValid(this.bitcoinURI)) {
25+
26+
var extraParams = [];
27+
if (this.isCounterparty) {
28+
extraParams.push('asset');
29+
}
30+
var isUriValid = URI.isValid(this.bitcoinURI, extraParams);
31+
if (!isUriValid) {
1632
this.error = true;
1733
return;
1834
}
19-
var uri = new URI(this.bitcoinURI);
35+
var uri = new URI(this.bitcoinURI, extraParams);
2036

2137
if (uri && uri.address) {
2238
var config = configService.getSync().wallet.settings;
2339
var unitToSatoshi = config.unitToSatoshi;
2440
var satToUnit = 1 / unitToSatoshi;
2541
var unitName = config.unitName;
42+
43+
//counterparty URI support
44+
if (uri.extras.asset && uri.extras.asset != 'BTC') {
45+
self.tokens.push(uri.extras.asset);
46+
self.loadBvam(function(err, bvamData) {
47+
if (err) { return $log.error(err); }
48+
self.bvamData = bvamData;
49+
});
50+
var listBvam = self.bvamData[uri.extras.asset] || {};
51+
uri.extras.asset = listBvam.short_name || listBvam.name || uri.extras.asset;
52+
}
2653

2754
if (uri.amount) {
28-
uri.amount = strip(uri.amount * satToUnit) + ' ' + unitName;
55+
if (!uri.extras.asset || uri.extras.asset == 'BTC') {
56+
uri.amount = delimitNumber(strip(uri.amount * satToUnit)) + ' ' + unitName;
57+
}
58+
else {
59+
uri.amount = delimitNumber(uri.amount / 100000000) + ' ' + uri.extras.asset;
60+
}
2961
}
62+
3063
uri.network = uri.address.network.name;
3164
this.uri = uri;
3265
}
3366
};
67+
68+
self.loadBvam = function(cb) {
69+
var tokenNames = self.tokens;
70+
bvamService.getBvamData(profileService.focusedCounterpartyClient, tokenNames, function(err, bvamData) {
71+
if (err) {
72+
return cb(err);
73+
}
74+
cb(null, bvamData);
75+
});
76+
}
3477

3578
this.getWallets = function(network) {
3679

@@ -49,7 +92,7 @@ angular.module('copayApp.controllers').controller('paymentUriController',
4992
profileService.setAndStoreFocus(wid, function() {});
5093
go.walletHome();
5194
$timeout(function() {
52-
$rootScope.$emit('paymentUri', self.bitcoinURI);
95+
$rootScope.$emit('paymentUri', self.origURI);
5396
}, 1000);
5497
};
5598
});

src/js/controllers/walletHome.js

+63-10
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
6666
return (b.charAt(0) > 0 && !(c || ".").lastIndexOf(".") ? b.replace(/(\d)(?=(\d{3})+$)/g, "$1,") : b) + c;
6767
});
6868
};
69+
70+
$scope.send_label = null;
71+
this.send_label = null;
6972

7073
var vanillaScope = ret;
7174

@@ -482,6 +485,19 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
482485
enumerable: true,
483486
configurable: true
484487
});
488+
489+
Object.defineProperty($scope,
490+
"_token", {
491+
get: function() {
492+
return $scope.__token;
493+
},
494+
set: function(newValue) {
495+
$scope.__token = newValue;
496+
self.resetError();
497+
},
498+
enumerable: true,
499+
configurable: true
500+
});
485501

486502
// [temporary] token property
487503
$scope.token = '';
@@ -632,6 +648,9 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
632648
else self.resetForm();
633649
});
634650
}
651+
652+
$scope.send_label = null;
653+
self.send_label = null;
635654
});
636655

637656
}, 100);
@@ -729,7 +748,7 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
729748
});
730749
};
731750

732-
this.setForm = function(to, amount, comment) {
751+
this.setForm = function(to, amount, comment, asset) {
733752
var form = $scope.sendForm;
734753
if (to) {
735754
form.address.$setViewValue(to);
@@ -750,6 +769,11 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
750769
form.comment.$isValid = true;
751770
form.comment.$render();
752771
}
772+
773+
if (asset) {
774+
this.lockSendToken = true;
775+
$scope._token = asset;
776+
}
753777
};
754778

755779
this.resetForm = function() {
@@ -760,8 +784,11 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
760784

761785
this.lockAddress = false;
762786
this.lockAmount = false;
787+
this.lockSendToken = false;
788+
789+
$scope.send_label = null;
763790

764-
this._amount = this._address = null;
791+
this._amount = this._address = null; this._token = null;
765792

766793
var form = $scope.sendForm;
767794

@@ -779,6 +806,13 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
779806
form.address.$setViewValue('');
780807
form.address.$render();
781808
}
809+
810+
if (form.token) {
811+
form.token.$pristine = tru;
812+
form.token.$setViewValue('BTC');
813+
form.token.$render();
814+
}
815+
$scope._token = 'BTC';
782816
}
783817
$timeout(function() {
784818
$rootScope.$digest();
@@ -898,6 +932,13 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
898932
};
899933

900934
var satToUnit = 1 / this.unitToSatoshi;
935+
936+
var isCounterparty = uri.includes('counterparty:');
937+
var uri = uri.replace('counterparty:', 'bitcoin:');
938+
var extraParams = [];
939+
if (this.isCounterparty) {
940+
extraParams.push('asset');
941+
}
901942

902943
// URI extensions for Payment Protocol with non-backwards-compatible request
903944
if ((/^bitcoin:\?r=[\w+]/).exec(uri)) {
@@ -910,27 +951,37 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
910951
} else {
911952
uri = sanitizeUri(uri);
912953

913-
if (!bitcore.URI.isValid(uri)) {
954+
if (!bitcore.URI.isValid(uri, extraParams)) {
914955
return uri;
915956
}
916-
var parsed = new bitcore.URI(uri);
957+
var parsed = new bitcore.URI(uri, extraParams);
958+
959+
if (parsed.label) {
960+
this.send_label = parsed.label;
961+
$scope.send_label = parsed.label;
962+
}
917963

918964
var addr = parsed.address ? parsed.address.toString() : '';
919965
var message = parsed.message;
966+
var asset = parsed.extras.asset || 'BTC';
920967

921-
var amount = parsed.amount ?
922-
(parsed.amount.toFixed(0) * satToUnit).toFixed(this.unitDecimals) : 0;
923-
968+
if (asset == 'BTC') {
969+
var amount = parsed.amount ?
970+
(parsed.amount.toFixed(0) * satToUnit).toFixed(this.unitDecimals) : 0;
971+
}
972+
else {
973+
var amount = (parsed.amount / 100000000).toFixed(8) || 0;
974+
}
924975

925976
if (parsed.r) {
926977
this.setFromPayPro(parsed.r, function(err) {
927978
if (err && addr && amount) {
928-
self.setForm(addr, amount, message);
979+
self.setForm(addr, amount, message, asset);
929980
return addr;
930981
}
931982
});
932983
} else {
933-
this.setForm(addr, amount, message);
984+
this.setForm(addr, amount, message, asset);
934985
return addr;
935986
}
936987
}
@@ -944,14 +995,16 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi
944995
if (this._paypro)
945996
return value;
946997

947-
if (value.indexOf('bitcoin:') === 0) {
998+
if (value.indexOf('bitcoin:') === 0 || value.indexOf('counterparty:') === 0) {
948999
return this.setFromUri(value);
9491000
} else if (/^https?:\/\//.test(value)) {
9501001
return this.setFromPayPro(value);
9511002
} else {
9521003
return value;
9531004
}
9541005
};
1006+
1007+
9551008

9561009
// History
9571010

webkitbuilds/.desktop

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ Terminal=false
99
Categories=Finance;Internet;
1010
X-Ubuntu-Touch=true
1111
X-Ubuntu-StageHint=SideStage
12-
MimeType=application/pockets;x-scheme-handler/pockets;application/bitcoin;x-scheme-handler/bitcoin;application/copay;x-scheme-handler/copay;
12+
MimeType=application/pockets;x-scheme-handler/pockets;application/bitcoin;x-scheme-handler/bitcoin;application/copay;x-scheme-handler/copay;application/counterparty;x-scheme-handler/counterparty;
1313

1414

webkitbuilds/setup-win.iss

+5
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,8 @@ Root: HKCR; Subkey: "pockets"; ValueType: "string"; ValueName: "URL Protocol"; V
6161
Root: HKCR; Subkey: "pockets\DefaultIcon"; ValueType: "string"; ValueData: "{app}\{#MyAppExeName},0"
6262
Root: HKCR; Subkey: "pockets\shell\open\command"; ValueType: "string"; ValueData: """{app}\{#MyAppExeName}"" ""%1"""
6363

64+
Root: HKCR; Subkey: "counterparty"; ValueType: "string"; ValueData: "URL:Counterparty Custom Protocol"; Flags: uninsdeletekey
65+
Root: HKCR; Subkey: "counterparty"; ValueType: "string"; ValueName: "URL Protocol"; ValueData: ""
66+
Root: HKCR; Subkey: "counterparty\DefaultIcon"; ValueType: "string"; ValueData: "{app}\{#MyAppExeName},0"
67+
Root: HKCR; Subkey: "counterparty\shell\open\command"; ValueType: "string"; ValueData: """{app}\{#MyAppExeName}"" ""%1"""
68+

0 commit comments

Comments
 (0)