Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge neurons #539

Merged
merged 28 commits into from
Mar 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5c9d56b
merge neuron UI
anishh2003 Feb 28, 2022
9d7d018
merge neuron UI
anishh2003 Mar 1, 2022
8ba2b17
Merge neurons initial setup
hpeebles Mar 2, 2022
5c55676
Add 'merge' to platform_ic_api
hpeebles Mar 2, 2022
4348bbf
neuron merge testing
anishh2003 Mar 3, 2022
54178b3
Merge branch 'main' of github.com:dfinity/dfinity_wallet into main4
hpeebles Mar 3, 2022
dc60947
A few merge neuron fixes
hpeebles Mar 4, 2022
7c1624a
Ensure neurons being merged have the same controller
hpeebles Mar 4, 2022
8b8be11
Pick target and source neurons to ensure age bonus is preserved
hpeebles Mar 4, 2022
c27bdf5
merge neuron will not display if less than 2 neurons. Fixed some code…
anishh2003 Mar 4, 2022
9dac0d5
Update text
hpeebles Mar 7, 2022
ad3f095
merge neuron with button disable
anishh2003 Mar 7, 2022
869d44d
Updating svelte frontend formatting
Mar 7, 2022
9639d32
pubspec not changed
anishh2003 Mar 7, 2022
970e47f
Merge remote-tracking branch 'origin/merge_neurons' into merge_neurons
anishh2003 Mar 7, 2022
f32d964
Delete pubspec.lock
anishh2003 Mar 7, 2022
d073e11
Revert "Delete pubspec.lock"
anishh2003 Mar 7, 2022
17a6f6d
Merge branch 'main' into merge_neurons
hpeebles Mar 7, 2022
a78e97d
Merge branch 'main' into merge_neurons
hpeebles Mar 7, 2022
e91d4f9
Merge branch 'main' into merge_neurons
hpeebles Mar 9, 2022
46cd103
Merge branch 'main' into merge_neurons
hpeebles Mar 11, 2022
7f7eacf
Update screenshots
Mar 11, 2022
9bcf24c
Merge branch 'main' into merge_neurons
hpeebles Mar 14, 2022
0cc40b9
Update screenshots
Mar 14, 2022
c5db218
Use Locked state rather than Dissolving to determine ordering
hpeebles Mar 15, 2022
18fb6f6
Merge branch 'main' into merge_neurons
hpeebles Mar 15, 2022
02cd66d
Merge branch 'main' into merge_neurons
hpeebles Mar 15, 2022
ad7e52b
Merge branch 'main' into merge_neurons
anishh2003 Mar 15, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions frontend/dart/lib/ic_api/platform_ic_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ abstract class AbstractPlatformICApi {
required ICP amount,
required String? toAccountId});

Future<Result<Unit, Exception>> merge(
{required Neuron neuron1, required Neuron neuron2});

Future<Result<Unit, Exception>> mergeMaturity(
{required Neuron neuron, required int percentageToMerge});

Expand Down
13 changes: 13 additions & 0 deletions frontend/dart/lib/ic_api/web/service_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ class ServiceApi {
@JS("disburseToNeuron")
external Promise<dynamic> disburseToNeuron(dynamic request);

@JS("merge")
external Promise<dynamic> merge(dynamic identity, dynamic request);

@JS("mergeMaturity")
external Promise<dynamic> mergeMaturity(dynamic identity, dynamic request);

Expand Down Expand Up @@ -223,6 +226,16 @@ class DisburseNeuronRequest {
{dynamic neuronId, dynamic amount, String? toAccountId});
}

@JS()
@anonymous
class MergeRequest {
external dynamic neuronId;
external dynamic sourceNeuronId;

external factory MergeRequest(
{dynamic neuronId, dynamic sourceNeuronId});
}

@JS()
@anonymous
class MergeMaturityRequest {
Expand Down
53 changes: 53 additions & 0 deletions frontend/dart/lib/ic_api/web/web_ic_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,59 @@ class PlatformICApi extends AbstractPlatformICApi {
}
}

@override
Future<Result<Unit, Exception>> merge(
{required Neuron neuron1, required Neuron neuron2}) async {
try {
final identity1 = (await getIdentityByNeuron(neuron1)).unwrap();
final identity2 = (await getIdentityByNeuron(neuron2)).unwrap();

if (identity1.getPrincipal().toString() != identity2.getPrincipal().toString()) {
return Result.err(Exception("The neurons being merged must both have the same controller"));
}

// To ensure any built up age bonus is always preserved, if one neuron is
// locked and the other is not, then we merge the neuron which is not
// locked into the locked neuron.
// In all other cases we merge the smaller neuron into the
// larger one.

final neuron1Locked = neuron1.state == NeuronState.LOCKED;
final neuron2Locked = neuron2.state == NeuronState.LOCKED;

Neuron targetNeuron;
Neuron sourceNeuron;
if (neuron1Locked != neuron2Locked) {
if (neuron1Locked) {
targetNeuron = neuron1;
sourceNeuron = neuron2;
} else {
targetNeuron = neuron2;
sourceNeuron = neuron1;
}
} else {
if (neuron1.cachedNeuronStake.asE8s() >= neuron2.cachedNeuronStake.asE8s()) {
targetNeuron = neuron1;
sourceNeuron = neuron2;
} else {
targetNeuron = neuron2;
sourceNeuron = neuron1;
}
}

await promiseToFuture(serviceApi!.merge(
identity1,
MergeRequest(
neuronId: targetNeuron.id.toBigInt.toJS,
sourceNeuronId: sourceNeuron.id.toBigInt.toJS)));
await fetchNeuron(neuronId: targetNeuron.id.toBigInt);
neuronSyncService!.removeNeuron(sourceNeuron.id.toString());
return Result.ok(unit);
} catch (err) {
return Result.err(Exception(err));
}
}

@override
Future<Result<Unit, Exception>> mergeMaturity(
{required Neuron neuron, required int percentageToMerge}) async {
Expand Down
19 changes: 11 additions & 8 deletions frontend/dart/lib/ui/neurons/tab/neuron_row.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import '../../../nns_dapp.dart';
class NeuronRow extends StatelessWidget {
final Neuron neuron;
final bool showsWarning;
final Function? onTap;

const NeuronRow(
{Key? key, required this.neuron, this.onTap, this.showsWarning = false})
const NeuronRow({Key? key, required this.neuron, this.showsWarning = false})
: super(key: key);

@override
Expand Down Expand Up @@ -89,15 +87,20 @@ class NeuronRow extends StatelessWidget {
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
"${neuron.dissolveDelay.yearsDayHourMinuteSecondFormatted()}",
style: context.textTheme.subtitle2),
Text(" Dissolve Delay", style: context.textTheme.subtitle2)
Expanded(
child: Text(
"${neuron.dissolveDelay.yearsDayHourMinuteSecondFormatted()}",
style: context.textTheme.subtitle2),
),
Expanded(
flex: 2,
child: Text(" Dissolve Delay",
style: context.textTheme.subtitle2))
],
),
SizedBox(
height: 5,
)
),
]
],
);
Expand Down
90 changes: 56 additions & 34 deletions frontend/dart/lib/ui/neurons/tab/neurons_tab_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:nns_dapp/ui/_components/footer_gradient_button.dart';
import 'package:nns_dapp/ui/_components/form_utils.dart';
import 'package:nns_dapp/ui/_components/page_button.dart';
import 'package:nns_dapp/ui/_components/tab_title_and_content.dart';
import 'package:nns_dapp/ui/transaction/wallet/merge_neuron_accounts_page.dart';
import 'package:nns_dapp/ui/transaction/wallet/select_source_wallet_page.dart';
import 'package:nns_dapp/ui/transaction/wizard_overlay.dart';
import '../../../nns_dapp.dart';
Expand All @@ -16,63 +17,84 @@ class NeuronsPage extends StatefulWidget {
class _NeuronsPageState extends State<NeuronsPage> {
@override
Widget build(BuildContext context) {
return FooterGradientButton(
footerHeight: null,
body: ConstrainWidthAndCenter(
child: StreamBuilder<void>(
stream: context.boxes.neurons.changes,
builder: (context, snapshot) {
return TabTitleAndContent(
return StreamBuilder<void>(
stream: context.boxes.neurons.changes,
builder: (context, snapshot) {
return FooterGradientButton(
footerHeight: null,
body: ConstrainWidthAndCenter(
child: TabTitleAndContent(
title: "Neurons",
subtitle:
'''Earn rewards by staking your ICP in neurons. Neurons allow you to participate in governance on the Internet Computer by voting on Network Nervous System (NNS) proposals.

Your principal id is "${context.icApi.getPrincipal()}"''',
children: [
SmallFormDivider(),
...(context.boxes.neurons.values
?.sortedByDescending((element) =>
element.createdTimestampSeconds.toBigInt)
?.sortedByDescending((element) => element.createdTimestampSeconds.toBigInt)
.mapToList((e) => Card(
child: TextButton(
onPressed: () {
context.nav.push(
neuronPageDef.createPageConfig(e));
context.nav.push(neuronPageDef.createPageConfig(e));
},
child: Padding(
padding: const EdgeInsets.all(16.0),
child: NeuronRow(
neuron: e,
showsWarning: true,
onTap: () {
context.nav.push(
neuronPageDef.createPageConfig(e));
},
),
),
),
)) ??
[]),
SizedBox(height: 150)
],
);
}),
),
footer: Align(
alignment: Alignment.bottomCenter,
child: PageButton(
title: "Stake Neuron",
onPress: () {
OverlayBaseWidget.show(
context,
WizardOverlay(
rootTitle: "Select Source Account",
rootWidget: SelectSourceWallet(isStakeNeuron: true),
),
);
},
),
),
);
),
footer: Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Flexible(
child: PageButton(
title: "Stake Neuron",
onPress: () {
OverlayBaseWidget.show(
context,
WizardOverlay(
rootTitle: "Select Source Account",
rootWidget: SelectSourceWallet(isStakeNeuron: true),
),
);
},
),
),
Flexible(
child: PageButton(
title: "Merge Neurons",
onPress: () {
OverlayBaseWidget.show(
context,
WizardOverlay(
rootTitle: "Select Two neurons to merge",
rootWidget: MergeNeuronSourceAccount(
onCompleteAction: (context) {
OverlayBaseWidget.of(context)?.dismiss();
},
),
),
);
}.takeIf((e) => context.boxes.neurons.values.length >= 2)),
),
],
),
),
),
);
});
}
}